@marcoscale98/pi-extensible-workflows 5.14.1-fork.1 → 5.19.1-fork.1
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 +135 -0
- package/dist/src/agent-execution.js +114 -87
- package/dist/src/background-widget.d.ts +61 -0
- package/dist/src/background-widget.js +23 -16
- package/dist/src/decoders.js +28 -1
- package/dist/src/execution.d.ts +4 -2
- package/dist/src/execution.js +43 -2
- package/dist/src/host-delivery.js +1 -0
- package/dist/src/host-navigator.d.ts +18 -0
- package/dist/src/host-navigator.js +71 -13
- package/dist/src/host-recovery.d.ts +5 -2
- package/dist/src/host-recovery.js +3 -3
- package/dist/src/host-view.d.ts +21 -1
- package/dist/src/host-view.js +138 -47
- package/dist/src/host.d.ts +2 -2
- package/dist/src/host.js +246 -60
- package/dist/src/index.d.ts +3 -1
- package/dist/src/index.js +1346 -622
- package/dist/src/index.js.map +3 -3
- package/dist/src/paths.d.ts +2 -0
- package/dist/src/paths.js +2 -0
- package/dist/src/registry.js +3 -2
- package/dist/src/roles.d.ts +70 -0
- package/dist/src/roles.js +326 -0
- package/dist/src/script-tools.d.ts +27 -0
- package/dist/src/script-tools.js +87 -0
- package/dist/src/settings.d.ts +26 -0
- package/dist/src/settings.js +277 -0
- package/dist/src/trajectory.d.ts +39 -4
- package/dist/src/trajectory.js +82 -5
- package/dist/src/types.d.ts +30 -3
- package/dist/src/types.js +1 -1
- package/dist/src/utils.d.ts +21 -1
- package/dist/src/utils.js +40 -0
- package/dist/src/validation.d.ts +10 -17
- package/dist/src/validation.js +29 -458
- package/dist/starter/index.js +0 -2
- package/dist/starter/index.js.map +1 -1
- package/dist/starter/review-loop.d.ts +0 -13
- package/dist/starter/review-loop.js +0 -2
- package/dist/subagents/index.js +1344 -798
- package/dist/subagents/index.js.map +3 -3
- package/dist/subagents/src/contracts.d.ts +49 -29
- package/dist/subagents/src/contracts.js +18 -7
- package/dist/subagents/src/index.d.ts +1 -1
- package/dist/subagents/src/index.js +15 -7
- package/dist/subagents/src/manager.js +32 -13
- package/dist/subagents/src/navigator.js +396 -247
- package/dist/subagents/src/view.d.ts +24 -2
- package/dist/subagents/src/view.js +107 -80
- package/dist/trajectory/assets/index.html +370 -232
- package/dist/trajectory/assets/prism.min.js +13 -0
- package/dist/trajectory/index.js +445 -277
- package/dist/trajectory/index.js.map +3 -3
- package/dist/trajectory/src/assets/index.html +370 -232
- package/dist/trajectory/src/assets/prism.min.js +13 -0
- package/dist/trajectory/src/export.js +4 -2
- package/dist/trajectory/src/index.d.ts +2 -0
- package/dist/trajectory/src/index.js +151 -60
- package/dist/trajectory/src/server.js +138 -11
- package/dist/trajectory/src/server.js.map +2 -2
- package/package.json +7 -6
- package/skills/pi-extensible-workflows/SKILL.md +2 -0
- package/src/agent-execution.ts +110 -80
- package/src/background-widget.ts +26 -19
- package/src/decoders.ts +22 -1
- package/src/execution.ts +41 -4
- package/src/host-delivery.ts +1 -0
- package/src/host-navigator.ts +58 -14
- package/src/host-recovery.ts +8 -7
- package/src/host-view.ts +116 -42
- package/src/host.ts +223 -55
- package/src/index.ts +3 -1
- package/src/paths.ts +3 -0
- package/src/registry.ts +3 -2
- package/src/roles.ts +327 -0
- package/src/script-tools.ts +76 -0
- package/src/settings.ts +224 -0
- package/src/trajectory.ts +97 -9
- package/src/types.ts +12 -5
- package/src/utils.ts +36 -1
- package/src/validation.ts +30 -356
- package/starter/review-loop.ts +0 -2
- package/subagents/README.md +3 -3
- package/subagents/src/contracts.ts +22 -7
- package/subagents/src/index.ts +18 -7
- package/subagents/src/manager.ts +31 -13
- package/subagents/src/navigator.ts +336 -180
- package/subagents/src/view.ts +108 -76
- package/trajectory/src/assets/index.html +370 -232
- package/trajectory/src/assets/prism.min.js +13 -0
- package/trajectory/src/export.ts +4 -2
- package/trajectory/src/index.ts +124 -47
- package/trajectory/src/server.ts +114 -14
package/dist/src/index.js
CHANGED
|
@@ -42,6 +42,7 @@ var ERROR_CODES = [
|
|
|
42
42
|
"AGENT_FAILED",
|
|
43
43
|
"AGENT_RESULT_COLLECTED",
|
|
44
44
|
"RESULT_INVALID",
|
|
45
|
+
"TOOL_FAILED",
|
|
45
46
|
"CANCELLED",
|
|
46
47
|
"WORKER_UNRESPONSIVE",
|
|
47
48
|
"WORKTREE_FAILED",
|
|
@@ -135,6 +136,7 @@ function createToolTimingExtension(clock = Date.now) {
|
|
|
135
136
|
}
|
|
136
137
|
|
|
137
138
|
// src/utils.ts
|
|
139
|
+
import { createHash } from "node:crypto";
|
|
138
140
|
import { Minimatch } from "minimatch";
|
|
139
141
|
var SerialLane = class {
|
|
140
142
|
#tail = Promise.resolve();
|
|
@@ -160,6 +162,14 @@ function isStringKey(key) {
|
|
|
160
162
|
function stringKeyValue(value, key) {
|
|
161
163
|
return object(value) ? value[key] : void 0;
|
|
162
164
|
}
|
|
165
|
+
function sortedJson(value) {
|
|
166
|
+
if (Array.isArray(value)) return value.map(sortedJson);
|
|
167
|
+
if (value === null || typeof value !== "object") return value;
|
|
168
|
+
return Object.fromEntries(Object.keys(value).sort().map((key) => [key, sortedJson(value[key])]));
|
|
169
|
+
}
|
|
170
|
+
function jsonDigest(value) {
|
|
171
|
+
return createHash("sha256").update(JSON.stringify(sortedJson(value))).digest("hex").slice(0, 16);
|
|
172
|
+
}
|
|
163
173
|
function jsonValue(value, seen = /* @__PURE__ */ new Set()) {
|
|
164
174
|
if (value === null || typeof value === "boolean" || typeof value === "string") return true;
|
|
165
175
|
if (typeof value === "number") return Number.isFinite(value);
|
|
@@ -320,6 +330,16 @@ function resolveModelReference(value, aliases = {}, knownModels, settingsPath) {
|
|
|
320
330
|
};
|
|
321
331
|
return resolveReference(value, []);
|
|
322
332
|
}
|
|
333
|
+
function reachableTools(pi) {
|
|
334
|
+
const undeclared = pi.getAllTools?.().filter(({ exposure }) => exposure === "codemode" || exposure === "deferred").map(({ name }) => name) ?? [];
|
|
335
|
+
return [.../* @__PURE__ */ new Set([...pi.getActiveTools(), ...undeclared])];
|
|
336
|
+
}
|
|
337
|
+
var VIRTUAL_MODEL_PROVIDER = "workflow";
|
|
338
|
+
function physicalModel(spec, aliases = {}, knownModels, settingsPath) {
|
|
339
|
+
if (spec.provider !== VIRTUAL_MODEL_PROVIDER || !modelAliasName(spec.model, aliases)) return spec;
|
|
340
|
+
const target = resolveModelReference(spec.model, aliases, knownModels, settingsPath);
|
|
341
|
+
return spec.thinking === void 0 ? target : { ...target, thinking: spec.thinking };
|
|
342
|
+
}
|
|
323
343
|
function modelCapability(value, aliases, knownModels, settingsPath) {
|
|
324
344
|
const parsed = typeof value === "string" ? resolveModelReference(value, aliases, knownModels, settingsPath) : value;
|
|
325
345
|
return `${parsed.provider}/${parsed.model}`;
|
|
@@ -370,9 +390,15 @@ function createLaunchSnapshot(input) {
|
|
|
370
390
|
function loadLaunchSnapshot(input) {
|
|
371
391
|
return deepFreeze(structuredClone(input));
|
|
372
392
|
}
|
|
393
|
+
function validateSchema(schema, at = "schema") {
|
|
394
|
+
if (!object(schema) || Object.getPrototypeOf(schema) !== Object.prototype || !jsonValue(schema)) fail("INVALID_SCHEMA", `${at} must be a plain JSON-compatible Schema object`);
|
|
395
|
+
if (typeof schema.type !== "string" && !Array.isArray(schema.type) && schema.$ref === void 0 && schema.anyOf === void 0 && schema.oneOf === void 0 && schema.allOf === void 0 && schema.const === void 0 && schema.enum === void 0) fail("INVALID_SCHEMA", `${at} has no JSON Schema shape`);
|
|
396
|
+
if (schema.required !== void 0 && (!Array.isArray(schema.required) || schema.required.some((key) => typeof key !== "string"))) fail("INVALID_SCHEMA", `${at}.required must be an array of strings`);
|
|
397
|
+
if (schema.properties !== void 0 && !object(schema.properties)) fail("INVALID_SCHEMA", `${at}.properties must be an object`);
|
|
398
|
+
}
|
|
373
399
|
|
|
374
400
|
// src/paths.ts
|
|
375
|
-
import { createHash } from "node:crypto";
|
|
401
|
+
import { createHash as createHash2 } from "node:crypto";
|
|
376
402
|
import { realpathSync } from "node:fs";
|
|
377
403
|
import { readdir } from "node:fs/promises";
|
|
378
404
|
import { homedir } from "node:os";
|
|
@@ -388,6 +414,9 @@ function canonicalPath(path) {
|
|
|
388
414
|
return absolute;
|
|
389
415
|
}
|
|
390
416
|
}
|
|
417
|
+
function extensionIdentity(path) {
|
|
418
|
+
return path.startsWith("builtin:") ? path : canonicalPath(path);
|
|
419
|
+
}
|
|
391
420
|
function sameFilesystemPath(left, right) {
|
|
392
421
|
return canonicalPath(left) === canonicalPath(right);
|
|
393
422
|
}
|
|
@@ -397,7 +426,7 @@ function workflowProjectSettingsPath(cwd) {
|
|
|
397
426
|
function projectStorageKey(cwd) {
|
|
398
427
|
const exact = resolve(cwd);
|
|
399
428
|
const slug = safePart(basename(exact)) || "root";
|
|
400
|
-
return `${slug}-${
|
|
429
|
+
return `${slug}-${createHash2("sha256").update(exact).digest("hex").slice(0, 12)}`;
|
|
401
430
|
}
|
|
402
431
|
function projectSessionsDirectory(cwd, home = homedir()) {
|
|
403
432
|
return join(home, ".pi", "workflows", "projects", projectStorageKey(cwd), "sessions");
|
|
@@ -670,6 +699,23 @@ function resumeBudgetAllowed(budget, usage) {
|
|
|
670
699
|
return exhaustedBudgetDimensions(budget, usage).length === 0;
|
|
671
700
|
}
|
|
672
701
|
|
|
702
|
+
// src/validation.ts
|
|
703
|
+
import { readFileSync as readFileSync4 } from "node:fs";
|
|
704
|
+
import { resolve as resolve5 } from "node:path";
|
|
705
|
+
import * as acorn from "acorn";
|
|
706
|
+
import { Script } from "node:vm";
|
|
707
|
+
|
|
708
|
+
// src/roles.ts
|
|
709
|
+
import { readFileSync as readFileSync3, readdirSync, statSync } from "node:fs";
|
|
710
|
+
import { basename as basename3, dirname as dirname4, extname, join as join6, resolve as resolve4 } from "node:path";
|
|
711
|
+
import { getAgentDir as getAgentDir2, parseFrontmatter } from "@earendil-works/pi-coding-agent";
|
|
712
|
+
|
|
713
|
+
// src/registry.ts
|
|
714
|
+
import { readFileSync as readFileSync2 } from "node:fs";
|
|
715
|
+
import { basename as basename2, dirname as dirname3, isAbsolute, join as join5 } from "node:path";
|
|
716
|
+
import { fileURLToPath as fileURLToPath2 } from "node:url";
|
|
717
|
+
import { Value } from "typebox/value";
|
|
718
|
+
|
|
673
719
|
// src/io.ts
|
|
674
720
|
import { execFile } from "node:child_process";
|
|
675
721
|
import { randomUUID } from "node:crypto";
|
|
@@ -690,7 +736,7 @@ async function renameWithRetry(from, to) {
|
|
|
690
736
|
} catch (error) {
|
|
691
737
|
const delayMs = RENAME_RETRY_DELAYS_MS[attempt];
|
|
692
738
|
if (!retryableRenameError(error) || delayMs === void 0) throw error;
|
|
693
|
-
await new Promise((
|
|
739
|
+
await new Promise((resolve9) => setTimeout(resolve9, delayMs));
|
|
694
740
|
}
|
|
695
741
|
}
|
|
696
742
|
}
|
|
@@ -894,7 +940,7 @@ function decodeWorkflowExtensions(value) {
|
|
|
894
940
|
}
|
|
895
941
|
if (namespace === "trajectory") {
|
|
896
942
|
if (!object(raw) || Object.keys(raw).some((key) => key !== "port" && key !== "themes") || raw.port !== void 0 && (typeof raw.port !== "number" || !Number.isSafeInteger(raw.port) || raw.port < 1 || raw.port > 65535) || raw.themes !== void 0 && typeof raw.themes !== "boolean") return void 0;
|
|
897
|
-
normalized.trajectory = { ...raw.port === void 0 ? {} : { port: raw.port }
|
|
943
|
+
normalized.trajectory = { ...raw.port === void 0 ? {} : { port: raw.port } };
|
|
898
944
|
continue;
|
|
899
945
|
}
|
|
900
946
|
if (!jsonValue(raw)) return void 0;
|
|
@@ -1178,6 +1224,22 @@ function decodeRunEvent(value) {
|
|
|
1178
1224
|
if (timestamp === INVALID_PERSISTED_VALUE) return void 0;
|
|
1179
1225
|
return { type: value.type, message: value.message, ...timestamp === void 0 ? {} : { timestamp } };
|
|
1180
1226
|
}
|
|
1227
|
+
function decodeScriptCall(value) {
|
|
1228
|
+
if (!object(value) || value.kind !== "shell" && value.kind !== "tool" || typeof value.name !== "string" || typeof value.path !== "string" || !finiteNumber(value.startedAt)) return void 0;
|
|
1229
|
+
const shell = value.kind === "shell";
|
|
1230
|
+
if (shell && !jsonValue(value.input)) return void 0;
|
|
1231
|
+
const inputBytes = shell ? optionalNumber(value.inputBytes) : void 0;
|
|
1232
|
+
const phase = optionalString(value.phase);
|
|
1233
|
+
const finishedAt = optionalNumber(value.finishedAt);
|
|
1234
|
+
let error;
|
|
1235
|
+
if (value.error !== void 0) {
|
|
1236
|
+
if (shell) error = decodeWorkflowError(value.error);
|
|
1237
|
+
else if (object(value.error) && typeof value.error.code === "string") error = { code: isWorkflowErrorCode(value.error.code) ? value.error.code : "INTERNAL_ERROR" };
|
|
1238
|
+
if (!error) return void 0;
|
|
1239
|
+
}
|
|
1240
|
+
if (inputBytes === INVALID_PERSISTED_VALUE || phase === INVALID_PERSISTED_VALUE || finishedAt === INVALID_PERSISTED_VALUE) return void 0;
|
|
1241
|
+
return { kind: value.kind, name: value.name, path: value.path, ...shell && jsonValue(value.input) ? { input: value.input } : {}, ...inputBytes === void 0 ? {} : { inputBytes }, ...phase === void 0 ? {} : { phase }, startedAt: value.startedAt, ...finishedAt === void 0 ? {} : { finishedAt }, ...error === void 0 ? {} : { error } };
|
|
1242
|
+
}
|
|
1181
1243
|
function decodeDelivery(value) {
|
|
1182
1244
|
if (!object(value)) return void 0;
|
|
1183
1245
|
const mode = value.mode;
|
|
@@ -1207,6 +1269,8 @@ function decodePersistedRun(value, allowLegacyAgentSessions = false) {
|
|
|
1207
1269
|
const budgetEvents = value.budgetEvents === void 0 ? void 0 : decodeArray(value.budgetEvents, decodeBudgetEvent);
|
|
1208
1270
|
const events = value.events === void 0 ? void 0 : decodeArray(value.events, decodeRunEvent);
|
|
1209
1271
|
const delivery = value.delivery === void 0 ? void 0 : decodeDelivery(value.delivery);
|
|
1272
|
+
const scriptCalls = value.scriptCalls === void 0 ? void 0 : decodeArray(value.scriptCalls, decodeScriptCall);
|
|
1273
|
+
if (value.scriptCalls !== void 0 && !scriptCalls) return void 0;
|
|
1210
1274
|
if (!agentSessions || !agents || parentRunId === INVALID_PERSISTED_VALUE || value.retry !== void 0 && !retry || phase === INVALID_PERSISTED_VALUE || value.phaseHistory !== void 0 && !phaseHistory || phaseHistoryIndex === INVALID_PERSISTED_VALUE || activeShells === INVALID_PERSISTED_VALUE || activeShellStartedAt === INVALID_PERSISTED_VALUE || value.activeShellsByPhase !== void 0 && !activeShellsByPhase || value.error !== void 0 && !error || failedAt === INVALID_PERSISTED_VALUE || value.budget !== void 0 && !budget || budgetVersion === INVALID_PERSISTED_VALUE || value.budgetVersion !== void 0 && !integer(budgetVersion) || value.usage !== void 0 && !usage || value.budgetEvents !== void 0 && !budgetEvents || value.events !== void 0 && !events || value.delivery !== void 0 && !delivery) return void 0;
|
|
1211
1275
|
if (value.agentSessions === void 0 && !allowLegacyAgentSessions) return void 0;
|
|
1212
1276
|
return {
|
|
@@ -1225,6 +1289,7 @@ function decodePersistedRun(value, allowLegacyAgentSessions = false) {
|
|
|
1225
1289
|
...activeShells === void 0 ? {} : { activeShells },
|
|
1226
1290
|
...activeShellStartedAt === void 0 ? {} : { activeShellStartedAt },
|
|
1227
1291
|
...activeShellsByPhase === void 0 ? {} : { activeShellsByPhase },
|
|
1292
|
+
...scriptCalls === void 0 ? {} : { scriptCalls },
|
|
1228
1293
|
...error === void 0 ? {} : { error },
|
|
1229
1294
|
...failedAt === void 0 ? {} : { failedAt },
|
|
1230
1295
|
...budget === void 0 ? {} : { budget },
|
|
@@ -1504,7 +1569,7 @@ async function listRunIds(cwd, sessionId, home = homedir2(), cleanTemps = true)
|
|
|
1504
1569
|
|
|
1505
1570
|
// src/store.ts
|
|
1506
1571
|
import { execFile as execFile2 } from "node:child_process";
|
|
1507
|
-
import { createHash as
|
|
1572
|
+
import { createHash as createHash3, randomUUID as randomUUID3 } from "node:crypto";
|
|
1508
1573
|
import { access, mkdir as mkdir2, readdir as readdir3, readFile as readFile3, realpath, rename as rename3, rm as rm3, stat as stat2, writeFile as writeFile2 } from "node:fs/promises";
|
|
1509
1574
|
import { homedir as homedir3 } from "node:os";
|
|
1510
1575
|
import { dirname, join as join3, relative, resolve as resolve2, sep } from "node:path";
|
|
@@ -1702,7 +1767,7 @@ var RunStore = class _RunStore {
|
|
|
1702
1767
|
}
|
|
1703
1768
|
}
|
|
1704
1769
|
async appendSystemPromptV2(entry) {
|
|
1705
|
-
const sha256 =
|
|
1770
|
+
const sha256 = createHash3("sha256").update(entry.prompt).digest("hex");
|
|
1706
1771
|
const bodyPath = join3(systemPromptBodiesPath(this.directory), sha256);
|
|
1707
1772
|
try {
|
|
1708
1773
|
await access(bodyPath);
|
|
@@ -1755,7 +1820,7 @@ var RunStore = class _RunStore {
|
|
|
1755
1820
|
const sha256 = record3.sha256;
|
|
1756
1821
|
if (typeof sessionId !== "string" || !sessionId || !positiveInteger(attempt) || !positiveInteger(turn) || typeof sha256 !== "string" || !/^[0-9a-f]{64}$/.test(sha256)) throw new Error("Persisted system-prompt record is invalid");
|
|
1757
1822
|
const prompt = await readFile3(join3(systemPromptBodiesPath(this.directory), sha256), "utf8");
|
|
1758
|
-
if (
|
|
1823
|
+
if (createHash3("sha256").update(prompt).digest("hex") !== sha256) throw new Error("Persisted system-prompt body is invalid");
|
|
1759
1824
|
entries.push({ sessionId, attempt, turn, sha256, prompt });
|
|
1760
1825
|
}
|
|
1761
1826
|
if (sequence < highest) throw new Error("Persisted system-prompt sequence is invalid");
|
|
@@ -1897,7 +1962,7 @@ var RunStore = class _RunStore {
|
|
|
1897
1962
|
});
|
|
1898
1963
|
}
|
|
1899
1964
|
#worktreeKey(owner) {
|
|
1900
|
-
return
|
|
1965
|
+
return createHash3("sha256").update(`${this.sessionId}\0${this.runId}\0${owner}`).digest("hex").slice(0, 16);
|
|
1901
1966
|
}
|
|
1902
1967
|
expectedWorktree(owner) {
|
|
1903
1968
|
const key = this.#worktreeKey(owner);
|
|
@@ -2356,21 +2421,250 @@ var RunStore = class _RunStore {
|
|
|
2356
2421
|
}
|
|
2357
2422
|
};
|
|
2358
2423
|
|
|
2359
|
-
// src/
|
|
2360
|
-
import { mkdirSync, readFileSync
|
|
2424
|
+
// src/settings.ts
|
|
2425
|
+
import { mkdirSync, readFileSync } from "node:fs";
|
|
2361
2426
|
import { homedir as homedir4 } from "node:os";
|
|
2362
|
-
import {
|
|
2363
|
-
import { fileURLToPath
|
|
2364
|
-
import
|
|
2365
|
-
|
|
2366
|
-
|
|
2427
|
+
import { dirname as dirname2, join as join4, resolve as resolve3 } from "node:path";
|
|
2428
|
+
import { fileURLToPath } from "node:url";
|
|
2429
|
+
import { getAgentDir } from "@earendil-works/pi-coding-agent";
|
|
2430
|
+
var ROLE_DIRECTORY = "pi-extensible-workflows";
|
|
2431
|
+
var DEFAULT_SETTINGS = Object.freeze({ concurrency: 8, backgroundWidget: true });
|
|
2432
|
+
function workflowSettingsPath(agentDir = getAgentDir()) {
|
|
2433
|
+
return join4(agentDir, ROLE_DIRECTORY, "settings.json");
|
|
2434
|
+
}
|
|
2435
|
+
function normalizedResourcePath(value, settingsPath) {
|
|
2436
|
+
if (value === "*" || value.startsWith("builtin:")) return value;
|
|
2437
|
+
let expanded = value === "~" ? homedir4() : value.startsWith("~/") || value.startsWith("~\\") ? join4(homedir4(), value.slice(2)) : value;
|
|
2438
|
+
if (expanded.startsWith("file://")) expanded = fileURLToPath(expanded);
|
|
2439
|
+
const resolved = resolve3(dirname2(settingsPath), expanded);
|
|
2440
|
+
if (expanded === "**" || expanded.startsWith("**/") || expanded.startsWith("**\\")) return expanded;
|
|
2441
|
+
if (resourcePatternHasMagic(expanded)) {
|
|
2442
|
+
const magicIndex = resolved.search(/[*?\x5b\x5d{}()]/);
|
|
2443
|
+
const separatorIndex = Math.max(resolved.lastIndexOf("/", magicIndex), resolved.lastIndexOf("\\", magicIndex));
|
|
2444
|
+
const rootBoundary = separatorIndex === 0 || separatorIndex === 2 && /^[A-Za-z]:[\\/]/.test(resolved);
|
|
2445
|
+
const prefix = rootBoundary ? resolved.slice(0, separatorIndex + 1) : separatorIndex >= 0 ? resolved.slice(0, separatorIndex) : resolved;
|
|
2446
|
+
const suffix = rootBoundary ? resolved.slice(separatorIndex + 1) : separatorIndex >= 0 ? resolved.slice(separatorIndex) : "";
|
|
2447
|
+
return `${canonicalPath(prefix)}${suffix}`;
|
|
2448
|
+
}
|
|
2449
|
+
return canonicalPath(resolved);
|
|
2450
|
+
}
|
|
2451
|
+
function validateSelectorList(value, path, kind, errorCode2 = "INVALID_SETTINGS", normalizeExtensions = true) {
|
|
2452
|
+
if (value === void 0) return void 0;
|
|
2453
|
+
if (!Array.isArray(value)) fail(errorCode2, `${path}.${kind} must be an array`);
|
|
2454
|
+
const normalized = [];
|
|
2455
|
+
for (const [index, entry] of value.entries()) {
|
|
2456
|
+
if (typeof entry !== "string" || !entry.trim()) fail(errorCode2, `${path}.${kind}[${String(index)}] must be a non-empty string`);
|
|
2457
|
+
let selector = entry.trim();
|
|
2458
|
+
if (kind === "extensions" && normalizeExtensions) {
|
|
2459
|
+
const negated = selector.startsWith("!");
|
|
2460
|
+
const body = negated ? selector.slice(1) : selector;
|
|
2461
|
+
if (!body) fail(errorCode2, `${path}.${kind}[${String(index)}] must be a valid minimatch pattern: Empty minimatch pattern ${JSON.stringify(selector)}`);
|
|
2462
|
+
try {
|
|
2463
|
+
selector = `${negated ? "!" : ""}${normalizedResourcePath(body, path)}`;
|
|
2464
|
+
} catch (error) {
|
|
2465
|
+
fail(errorCode2, `${path}.${kind}[${String(index)}] must be a valid path: ${errorText(error)}`);
|
|
2466
|
+
}
|
|
2467
|
+
}
|
|
2468
|
+
try {
|
|
2469
|
+
validateResourcePattern(selector);
|
|
2470
|
+
} catch (error) {
|
|
2471
|
+
fail(errorCode2, `${path}.${kind}[${String(index)}] must be a valid minimatch pattern: ${errorText(error)}`);
|
|
2472
|
+
}
|
|
2473
|
+
normalized.push(selector);
|
|
2474
|
+
}
|
|
2475
|
+
return Object.freeze(normalized);
|
|
2476
|
+
}
|
|
2477
|
+
function validateWorktreePostCreateCommand(value, settingsPath) {
|
|
2478
|
+
if (value === void 0) return void 0;
|
|
2479
|
+
if (!Array.isArray(value) || value.length === 0) fail("INVALID_SETTINGS", `${settingsPath}.worktreePostCreateCommand must be a non-empty array`);
|
|
2480
|
+
return Object.freeze(value.map((entry, index) => {
|
|
2481
|
+
if (typeof entry !== "string" || !entry.trim()) fail("INVALID_SETTINGS", `${settingsPath}.worktreePostCreateCommand[${String(index)}] must be a non-empty string`);
|
|
2482
|
+
return entry;
|
|
2483
|
+
}));
|
|
2484
|
+
}
|
|
2485
|
+
function selectorsFromSettings(settings) {
|
|
2486
|
+
return {
|
|
2487
|
+
...settings.skills === void 0 ? {} : { skills: settings.skills },
|
|
2488
|
+
...settings.extensions === void 0 ? {} : { extensions: settings.extensions },
|
|
2489
|
+
...settings.tools === void 0 ? {} : { tools: settings.tools }
|
|
2490
|
+
};
|
|
2491
|
+
}
|
|
2492
|
+
function selectorSet(value) {
|
|
2493
|
+
return { skills: [...value?.skills ?? []], extensions: [...value?.extensions ?? []], ...value?.tools === void 0 ? {} : { tools: [...value.tools] } };
|
|
2494
|
+
}
|
|
2495
|
+
function validateContextFileScopes(value, rolePath) {
|
|
2496
|
+
if (value === void 0) return void 0;
|
|
2497
|
+
if (!Array.isArray(value) || !value.every(isContextFileScope)) fail("INVALID_METADATA", `${rolePath}.contextFiles must be an array containing only global, project, or cwd`);
|
|
2498
|
+
return [...value];
|
|
2499
|
+
}
|
|
2500
|
+
function validateWorkflowExtensionSettings(value, settingsPath, errorCode2 = "INVALID_SETTINGS") {
|
|
2501
|
+
if (value === void 0) return void 0;
|
|
2502
|
+
const base = `${settingsPath}.extensionSettings`;
|
|
2503
|
+
if (!object(value)) fail(errorCode2, `${base} must be an object`);
|
|
2504
|
+
const normalized = {};
|
|
2505
|
+
for (const [namespace, raw] of Object.entries(value)) {
|
|
2506
|
+
if (!validWorkflowExtensionNamespace(namespace)) fail(errorCode2, `${base} contains an invalid namespace: ${namespace}`);
|
|
2507
|
+
if (namespace === "herdr") {
|
|
2508
|
+
if (!object(raw)) fail(errorCode2, `${base}.herdr must be an object`);
|
|
2509
|
+
if (Object.keys(raw).some((key) => key !== "enableFullyInspectableMode")) fail(errorCode2, `${base}.herdr contains an unsupported setting`);
|
|
2510
|
+
if (raw.enableFullyInspectableMode !== void 0 && typeof raw.enableFullyInspectableMode !== "boolean") fail(errorCode2, `${base}.herdr.enableFullyInspectableMode must be a boolean`);
|
|
2511
|
+
normalized.herdr = Object.freeze({ ...raw.enableFullyInspectableMode === void 0 ? {} : { enableFullyInspectableMode: raw.enableFullyInspectableMode } });
|
|
2512
|
+
continue;
|
|
2513
|
+
}
|
|
2514
|
+
if (namespace === "trajectory") {
|
|
2515
|
+
if (!object(raw)) fail(errorCode2, `${base}.trajectory must be an object`);
|
|
2516
|
+
if (Object.keys(raw).some((key) => key !== "port")) fail(errorCode2, `${base}.trajectory contains an unsupported setting`);
|
|
2517
|
+
if (raw.port !== void 0 && (!positiveInteger(raw.port) || raw.port > 65535)) fail(errorCode2, `${base}.trajectory.port must be an integer from 1 to 65535`);
|
|
2518
|
+
normalized.trajectory = Object.freeze({ ...raw.port === void 0 ? {} : { port: raw.port } });
|
|
2519
|
+
continue;
|
|
2520
|
+
}
|
|
2521
|
+
if (!jsonValue(raw)) fail(errorCode2, `${base}.${namespace} must be JSON-compatible`);
|
|
2522
|
+
normalized[namespace] = structuredClone(raw);
|
|
2523
|
+
}
|
|
2524
|
+
return deepFreeze(normalized);
|
|
2525
|
+
}
|
|
2526
|
+
function positiveRetentionInteger(value) {
|
|
2527
|
+
return positiveInteger(value) && Number.isSafeInteger(value);
|
|
2528
|
+
}
|
|
2529
|
+
function validateRetention(value, settingsPath) {
|
|
2530
|
+
if (value === void 0) return void 0;
|
|
2531
|
+
const base = `${settingsPath}.retention`;
|
|
2532
|
+
if (!object(value)) fail("INVALID_SETTINGS", `${base} must be an object`);
|
|
2533
|
+
const unknown = Object.keys(value).find((key) => key !== "olderThanDays" && key !== "maxTerminalRuns");
|
|
2534
|
+
if (unknown) fail("INVALID_SETTINGS", `Unknown retention setting at ${base}: ${unknown}`);
|
|
2535
|
+
if (value.olderThanDays !== void 0 && !positiveRetentionInteger(value.olderThanDays)) fail("INVALID_SETTINGS", `${base}.olderThanDays must be a positive integer`);
|
|
2536
|
+
if (value.maxTerminalRuns !== void 0 && !positiveRetentionInteger(value.maxTerminalRuns)) fail("INVALID_SETTINGS", `${base}.maxTerminalRuns must be a positive integer`);
|
|
2537
|
+
return Object.freeze({ ...value.olderThanDays === void 0 ? {} : { olderThanDays: value.olderThanDays }, ...value.maxTerminalRuns === void 0 ? {} : { maxTerminalRuns: value.maxTerminalRuns } });
|
|
2538
|
+
}
|
|
2539
|
+
function parseSettings(path, partial) {
|
|
2540
|
+
let parsed;
|
|
2541
|
+
try {
|
|
2542
|
+
parsed = JSON.parse(readFileSync(path, "utf8"));
|
|
2543
|
+
} catch (error) {
|
|
2544
|
+
if (isNodeError(error, "ENOENT")) return partial ? Object.freeze({}) : DEFAULT_SETTINGS;
|
|
2545
|
+
fail("CONFIG_ERROR", `Invalid workflow settings JSON at ${path}: ${errorText(error)}`);
|
|
2546
|
+
}
|
|
2547
|
+
if (!object(parsed)) fail("INVALID_SETTINGS", `Workflow settings at ${path} must be an object`);
|
|
2548
|
+
const allowed = /* @__PURE__ */ new Set(["concurrency", "modelAliases", "skills", "extensions", "extensionSettings", "tools", "retention", "worktreePostCreateCommand", ...partial ? [] : ["backgroundWidget", "codemodeTools"]]);
|
|
2549
|
+
const unknown = Object.keys(parsed).find((key) => !allowed.has(key));
|
|
2550
|
+
if (Object.prototype.hasOwnProperty.call(parsed, "disabledAgentResources")) fail("INVALID_SETTINGS", `disabledAgentResources is no longer supported; use skills, extensions, and tools selectors (settings: ${path})`);
|
|
2551
|
+
if (unknown) fail("INVALID_SETTINGS", `Unknown workflow setting at ${path}: ${unknown}`);
|
|
2552
|
+
const concurrency = parsed.concurrency === void 0 ? partial ? void 0 : DEFAULT_SETTINGS.concurrency : parsed.concurrency;
|
|
2553
|
+
if (concurrency !== void 0 && (!positiveInteger(concurrency) || concurrency > 16)) fail("INVALID_SETTINGS", `${path}.concurrency must be an integer from 1 to 16`);
|
|
2554
|
+
const backgroundWidget = parsed.backgroundWidget === void 0 ? partial ? void 0 : DEFAULT_SETTINGS.backgroundWidget : parsed.backgroundWidget;
|
|
2555
|
+
if (backgroundWidget !== void 0 && typeof backgroundWidget !== "boolean") fail("INVALID_SETTINGS", `${path}.backgroundWidget must be a boolean`);
|
|
2556
|
+
const codemodeTools = parsed.codemodeTools;
|
|
2557
|
+
if (codemodeTools !== void 0 && codemodeTools !== "all" && codemodeTools !== "read-only" && codemodeTools !== "none") fail("INVALID_SETTINGS", `${path}.codemodeTools must be "all", "read-only", or "none"`);
|
|
2558
|
+
const modelAliases = parsed.modelAliases === void 0 ? void 0 : validateModelAliases(parsed.modelAliases, path);
|
|
2559
|
+
const skills = validateSelectorList(parsed.skills, path, "skills");
|
|
2560
|
+
const tools = validateSelectorList(parsed.tools, path, "tools");
|
|
2561
|
+
const extensions = validateSelectorList(parsed.extensions, path, "extensions");
|
|
2562
|
+
const extensionSettings = parsed.extensionSettings === void 0 ? void 0 : validateWorkflowExtensionSettings(parsed.extensionSettings, path, "INVALID_SETTINGS");
|
|
2563
|
+
const retention = validateRetention(parsed.retention, path);
|
|
2564
|
+
const worktreePostCreateCommand = validateWorktreePostCreateCommand(parsed.worktreePostCreateCommand, path);
|
|
2565
|
+
return Object.freeze({
|
|
2566
|
+
...concurrency === void 0 ? {} : { concurrency },
|
|
2567
|
+
...backgroundWidget === void 0 ? {} : { backgroundWidget },
|
|
2568
|
+
...codemodeTools === void 0 ? {} : { codemodeTools },
|
|
2569
|
+
...modelAliases === void 0 ? {} : { modelAliases },
|
|
2570
|
+
...skills === void 0 ? {} : { skills },
|
|
2571
|
+
...extensions === void 0 ? {} : { extensions },
|
|
2572
|
+
...extensionSettings === void 0 ? {} : { extensionSettings },
|
|
2573
|
+
...tools === void 0 ? {} : { tools },
|
|
2574
|
+
...retention === void 0 ? {} : { retention },
|
|
2575
|
+
...worktreePostCreateCommand === void 0 ? {} : { worktreePostCreateCommand }
|
|
2576
|
+
});
|
|
2577
|
+
}
|
|
2578
|
+
function loadSettings(path = workflowSettingsPath()) {
|
|
2579
|
+
return parseSettings(path, false);
|
|
2580
|
+
}
|
|
2581
|
+
var READ_ONLY_WORKFLOW_TOOLS = /* @__PURE__ */ new Set(["workflow_status", "workflow_catalog", "subagents_inspect"]);
|
|
2582
|
+
function workflowToolExposure(name, codemodeTools) {
|
|
2583
|
+
const readOnly = READ_ONLY_WORKFLOW_TOOLS.has(name);
|
|
2584
|
+
return { ...codemodeTools === "none" || codemodeTools === "read-only" && !readOnly ? { exposure: "model-only" } : {}, ...readOnly ? { annotations: { readOnlyHint: true } } : {} };
|
|
2585
|
+
}
|
|
2586
|
+
function loadCodemodeToolsSetting(agentDir) {
|
|
2587
|
+
try {
|
|
2588
|
+
return loadSettings(workflowSettingsPath(agentDir)).codemodeTools;
|
|
2589
|
+
} catch {
|
|
2590
|
+
return void 0;
|
|
2591
|
+
}
|
|
2592
|
+
}
|
|
2593
|
+
function loadSettingsOverrides(path) {
|
|
2594
|
+
return parseSettings(path, true);
|
|
2595
|
+
}
|
|
2596
|
+
function resolveWorkflowSettings(cwd, projectTrusted2, globalSettingsPath = workflowSettingsPath()) {
|
|
2597
|
+
const projectSettingsPath = workflowProjectSettingsPath(cwd);
|
|
2598
|
+
const global = loadSettings(globalSettingsPath);
|
|
2599
|
+
const project = projectTrusted2 ? loadSettingsOverrides(projectSettingsPath) : Object.freeze({});
|
|
2600
|
+
const projectHas = (key) => Object.prototype.hasOwnProperty.call(project, key);
|
|
2601
|
+
const sourceFor = (key) => projectHas(key) ? projectSettingsPath : globalSettingsPath;
|
|
2602
|
+
const globalSelectors = selectorsFromSettings(global);
|
|
2603
|
+
const projectSelectors = selectorsFromSettings(project);
|
|
2604
|
+
const effectiveSelectors = selectorSet({
|
|
2605
|
+
skills: [...globalSelectors.skills ?? [], ...projectSelectors.skills ?? []],
|
|
2606
|
+
extensions: [...globalSelectors.extensions ?? [], ...projectSelectors.extensions ?? []],
|
|
2607
|
+
...globalSelectors.tools === void 0 && projectSelectors.tools === void 0 ? {} : { tools: [...globalSelectors.tools ?? [], ...projectSelectors.tools ?? []] }
|
|
2608
|
+
});
|
|
2609
|
+
const hasExtensionSelectors = global.extensions !== void 0 || project.extensions !== void 0;
|
|
2610
|
+
const extensionSettings = projectHas("extensionSettings") ? project.extensionSettings : global.extensionSettings;
|
|
2611
|
+
const sources = {
|
|
2612
|
+
concurrency: projectHas("concurrency") ? projectSettingsPath : globalSettingsPath,
|
|
2613
|
+
modelAliases: projectHas("modelAliases") ? projectSettingsPath : globalSettingsPath,
|
|
2614
|
+
skills: sourceFor("skills"),
|
|
2615
|
+
extensions: sourceFor("extensions"),
|
|
2616
|
+
tools: sourceFor("tools"),
|
|
2617
|
+
...extensionSettings === void 0 ? {} : { extensionSettings: projectHas("extensionSettings") ? projectSettingsPath : globalSettingsPath },
|
|
2618
|
+
...project.retention === void 0 && global.retention === void 0 ? {} : { retention: project.retention === void 0 ? globalSettingsPath : projectSettingsPath },
|
|
2619
|
+
...projectHas("worktreePostCreateCommand") || global.worktreePostCreateCommand !== void 0 ? { worktreePostCreateCommand: projectHas("worktreePostCreateCommand") ? projectSettingsPath : globalSettingsPath } : {}
|
|
2620
|
+
};
|
|
2621
|
+
const effective = Object.freeze({
|
|
2622
|
+
concurrency: project.concurrency ?? global.concurrency,
|
|
2623
|
+
backgroundWidget: global.backgroundWidget ?? true,
|
|
2624
|
+
...projectHas("modelAliases") ? { modelAliases: project.modelAliases } : global.modelAliases === void 0 ? {} : { modelAliases: global.modelAliases },
|
|
2625
|
+
...effectiveSelectors.skills.length ? { skills: effectiveSelectors.skills } : global.skills !== void 0 || project.skills !== void 0 ? { skills: effectiveSelectors.skills } : {},
|
|
2626
|
+
...hasExtensionSelectors ? { extensions: effectiveSelectors.extensions } : {},
|
|
2627
|
+
...extensionSettings === void 0 ? {} : { extensionSettings },
|
|
2628
|
+
...effectiveSelectors.tools?.length ? { tools: effectiveSelectors.tools } : global.tools !== void 0 || project.tools !== void 0 ? { tools: effectiveSelectors.tools } : {},
|
|
2629
|
+
...(project.retention ?? global.retention) === void 0 ? {} : { retention: project.retention ?? global.retention },
|
|
2630
|
+
...projectHas("worktreePostCreateCommand") ? { worktreePostCreateCommand: project.worktreePostCreateCommand } : global.worktreePostCreateCommand === void 0 ? {} : { worktreePostCreateCommand: global.worktreePostCreateCommand }
|
|
2631
|
+
});
|
|
2632
|
+
return { globalSettingsPath, projectSettingsPath, projectTrusted: projectTrusted2, global, project, effective, sources };
|
|
2633
|
+
}
|
|
2634
|
+
function validateModelAliasAvailability(aliases, names, availableModels, knownModels, settingsPath) {
|
|
2635
|
+
for (const name of names) {
|
|
2636
|
+
try {
|
|
2637
|
+
const target = modelCapability(name, aliases, knownModels, settingsPath);
|
|
2638
|
+
if (!availableModels.has(target)) unknownModel(name, target, settingsPath);
|
|
2639
|
+
} catch (error) {
|
|
2640
|
+
throw annotateModelAliasError(error, name);
|
|
2641
|
+
}
|
|
2642
|
+
}
|
|
2643
|
+
}
|
|
2644
|
+
function resolveAgentResourcePolicy(cwd, projectTrusted2, globalSettingsPath = workflowSettingsPath()) {
|
|
2645
|
+
const resolved = resolveWorkflowSettings(cwd, projectTrusted2, globalSettingsPath);
|
|
2646
|
+
const global = selectorSet(selectorsFromSettings(resolved.global));
|
|
2647
|
+
const project = selectorSet(selectorsFromSettings(resolved.project));
|
|
2648
|
+
const effective = selectorSet(selectorsFromSettings(resolved.effective));
|
|
2649
|
+
return { globalSettingsPath: resolved.globalSettingsPath, projectSettingsPath: resolved.projectSettingsPath, projectTrusted: projectTrusted2, global, project, effective, unmatchedSkills: [], unmatchedExtensions: [], unmatchedTools: [], selectorSources: { global: selectorsFromSettings(resolved.global), project: selectorsFromSettings(resolved.project) } };
|
|
2650
|
+
}
|
|
2651
|
+
function saveModelAliases(path = workflowSettingsPath(), aliases = {}) {
|
|
2652
|
+
const normalized = validateModelAliases(aliases, path);
|
|
2653
|
+
let parsed = {};
|
|
2654
|
+
try {
|
|
2655
|
+
loadSettings(path);
|
|
2656
|
+
parsed = JSON.parse(readFileSync(path, "utf8"));
|
|
2657
|
+
} catch (error) {
|
|
2658
|
+
if (!isNodeError(error, "ENOENT")) throw error;
|
|
2659
|
+
}
|
|
2660
|
+
if (!object(parsed)) fail("INVALID_SETTINGS", `Workflow settings at ${path} must be an object`);
|
|
2661
|
+
mkdirSync(dirname2(path), { recursive: true });
|
|
2662
|
+
atomicWriteFile(path, `${JSON.stringify({ ...parsed, modelAliases: normalized }, null, 2)}
|
|
2663
|
+
`, true);
|
|
2664
|
+
}
|
|
2367
2665
|
|
|
2368
2666
|
// src/registry.ts
|
|
2369
|
-
|
|
2370
|
-
import { basename as basename2, dirname as dirname2, isAbsolute, join as join4 } from "node:path";
|
|
2371
|
-
import { fileURLToPath } from "node:url";
|
|
2372
|
-
import { Value } from "typebox/value";
|
|
2373
|
-
var RESERVED_GLOBALS = /* @__PURE__ */ new Set(["agent", "shell", "prompt", "checkpoint", "parallel", "pipeline", "phase", "withWorktree", "log", "args", "Promise", "JSON", "Math", "Date", "eval", "Function", "WebAssembly", "process", "require", "module", "exports", "console", "fetch", "XMLHttpRequest", "WebSocket", "performance", "crypto", "setTimeout", "setInterval", "setImmediate", "queueMicrotask", "Intl", "SharedArrayBuffer", "Atomics", "globalThis", "global", "undefined", "NaN", "Infinity", "extensions", "workflow_catalog"]);
|
|
2667
|
+
var RESERVED_GLOBALS = /* @__PURE__ */ new Set(["agent", "shell", "tools", "prompt", "checkpoint", "parallel", "pipeline", "phase", "withWorktree", "log", "args", "Promise", "JSON", "Math", "Date", "eval", "Function", "WebAssembly", "process", "require", "module", "exports", "console", "fetch", "XMLHttpRequest", "WebSocket", "performance", "crypto", "setTimeout", "setInterval", "setImmediate", "queueMicrotask", "Intl", "SharedArrayBuffer", "Atomics", "globalThis", "global", "undefined", "NaN", "Infinity", "extensions", "workflow_catalog"]);
|
|
2374
2668
|
var IDENTIFIER = /^[A-Za-z_$][\w$]*$/;
|
|
2375
2669
|
var SEMVER = /^\d+\.\d+\.\d+(?:-[0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*)?(?:\+[0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*)?$/;
|
|
2376
2670
|
var PACKAGE_NAME = /^(?:@[a-z0-9][a-z0-9._-]*\/)?[a-z0-9][a-z0-9._-]*$/;
|
|
@@ -2378,10 +2672,10 @@ function normalizeRoleDirectory(value) {
|
|
|
2378
2672
|
try {
|
|
2379
2673
|
if (value instanceof URL) {
|
|
2380
2674
|
if (value.protocol !== "file:") fail("INVALID_METADATA", "Workflow role directories require file URLs or absolute filesystem paths");
|
|
2381
|
-
return
|
|
2675
|
+
return fileURLToPath2(value);
|
|
2382
2676
|
}
|
|
2383
2677
|
if (typeof value === "string") {
|
|
2384
|
-
if (value.startsWith("file:")) return
|
|
2678
|
+
if (value.startsWith("file:")) return fileURLToPath2(value);
|
|
2385
2679
|
if (isAbsolute(value)) return value;
|
|
2386
2680
|
}
|
|
2387
2681
|
} catch (error) {
|
|
@@ -2391,12 +2685,12 @@ function normalizeRoleDirectory(value) {
|
|
|
2391
2685
|
}
|
|
2392
2686
|
function isBuiltinRoleDirectory(path) {
|
|
2393
2687
|
const canonical = canonicalPath(path);
|
|
2394
|
-
const starterDirectory =
|
|
2688
|
+
const starterDirectory = dirname3(canonical);
|
|
2395
2689
|
if (basename2(canonical) !== "roles" || basename2(starterDirectory) !== "starter") return false;
|
|
2396
|
-
const distributionDirectory =
|
|
2397
|
-
const packageRoot2 = basename2(distributionDirectory) === "dist" ?
|
|
2690
|
+
const distributionDirectory = dirname3(starterDirectory);
|
|
2691
|
+
const packageRoot2 = basename2(distributionDirectory) === "dist" ? dirname3(distributionDirectory) : distributionDirectory;
|
|
2398
2692
|
try {
|
|
2399
|
-
const metadata = JSON.parse(
|
|
2693
|
+
const metadata = JSON.parse(readFileSync2(join5(packageRoot2, "package.json"), "utf8"));
|
|
2400
2694
|
return object(metadata) && isCorePackageName(metadata.name);
|
|
2401
2695
|
} catch {
|
|
2402
2696
|
return false;
|
|
@@ -2669,278 +2963,54 @@ function resetWorkflowRegistryIfIdle() {
|
|
|
2669
2963
|
}
|
|
2670
2964
|
function beginWorkflowExtensionLoading() {
|
|
2671
2965
|
const host = workflowRegistryHost();
|
|
2672
|
-
if (host.api.frozen && host.activeHosts === 0) resetWorkflowRegistry();
|
|
2673
|
-
}
|
|
2674
|
-
function retainWorkflowRegistry() {
|
|
2675
|
-
const host = workflowRegistryHost();
|
|
2676
|
-
host.activeHosts += 1;
|
|
2677
|
-
let retained = true;
|
|
2678
|
-
return () => {
|
|
2679
|
-
if (!retained) return;
|
|
2680
|
-
retained = false;
|
|
2681
|
-
host.activeHosts -= 1;
|
|
2682
|
-
if (host.activeHosts === 0) resetWorkflowRegistry();
|
|
2683
|
-
};
|
|
2684
|
-
}
|
|
2685
|
-
function loadingRegistry() {
|
|
2686
|
-
return workflowRegistryHost().api;
|
|
2687
|
-
}
|
|
2688
|
-
beginWorkflowExtensionLoading();
|
|
2689
|
-
function registerWorkflowExtension(extension) {
|
|
2690
|
-
loadingRegistry().register(extension);
|
|
2691
|
-
}
|
|
2692
|
-
function workflowCatalog(context) {
|
|
2693
|
-
return loadingRegistry().catalog(context);
|
|
2694
|
-
}
|
|
2695
|
-
function workflowCatalogIndex(context) {
|
|
2696
|
-
return loadingRegistry().catalogIndex(context);
|
|
2697
|
-
}
|
|
2698
|
-
function workflowCatalogDetail(name, context) {
|
|
2699
|
-
return loadingRegistry().catalogDetail(name, context);
|
|
2700
|
-
}
|
|
2701
|
-
function registeredWorkflowFunctions() {
|
|
2702
|
-
return loadingRegistry().functions();
|
|
2703
|
-
}
|
|
2704
|
-
function registeredWorkflowFunctionSources() {
|
|
2705
|
-
const sources = loadingRegistry().functionSources;
|
|
2706
|
-
return typeof sources === "function" ? sources() : {};
|
|
2707
|
-
}
|
|
2708
|
-
function registeredWorkflowRoleDirectories() {
|
|
2709
|
-
const directories = loadingRegistry().roleDirectories;
|
|
2710
|
-
return typeof directories === "function" ? directories() : [];
|
|
2711
|
-
}
|
|
2712
|
-
function registeredWorkflowRoleDirectoryRegistrations() {
|
|
2713
|
-
const registrations = loadingRegistry().roleDirectoryRegistrations;
|
|
2714
|
-
return typeof registrations === "function" ? registrations() : [];
|
|
2715
|
-
}
|
|
2716
|
-
|
|
2717
|
-
// src/validation.ts
|
|
2718
|
-
var DEFAULT_SETTINGS = Object.freeze({ concurrency: 8, backgroundWidget: true });
|
|
2719
|
-
function validateCheckpoint(value) {
|
|
2720
|
-
if (!object(value) || Object.keys(value).some((key) => !["name", "prompt", "context"].includes(key)) || typeof value.name !== "string" || value.name.trim() === "" || typeof value.prompt !== "string" || !jsonValue(value.context)) fail("INVALID_METADATA", "checkpoint requires only name, prompt, and JSON context");
|
|
2721
|
-
if (Buffer.byteLength(value.prompt) > 1024) fail("INVALID_METADATA", "checkpoint prompt exceeds 1024 UTF-8 bytes");
|
|
2722
|
-
if (Buffer.byteLength(JSON.stringify(value.context)) > 4096) fail("INVALID_METADATA", "checkpoint context exceeds 4096 UTF-8 bytes");
|
|
2723
|
-
return { name: value.name, prompt: value.prompt, context: value.context };
|
|
2724
|
-
}
|
|
2725
|
-
function workflowSettingsPath(agentDir = getAgentDir()) {
|
|
2726
|
-
return join5(agentDir, ROLE_DIRECTORY, "settings.json");
|
|
2727
|
-
}
|
|
2728
|
-
function normalizedResourcePath(value, settingsPath) {
|
|
2729
|
-
if (value === "*") return value;
|
|
2730
|
-
let expanded = value === "~" ? homedir4() : value.startsWith("~/") || value.startsWith("~\\") ? join5(homedir4(), value.slice(2)) : value;
|
|
2731
|
-
if (expanded.startsWith("file://")) expanded = fileURLToPath2(expanded);
|
|
2732
|
-
const resolved = resolve3(dirname3(settingsPath), expanded);
|
|
2733
|
-
if (expanded === "**" || expanded.startsWith("**/") || expanded.startsWith("**\\")) return expanded;
|
|
2734
|
-
if (resourcePatternHasMagic(expanded)) {
|
|
2735
|
-
const magicIndex = resolved.search(/[*?\x5b\x5d{}()]/);
|
|
2736
|
-
const separatorIndex = Math.max(resolved.lastIndexOf("/", magicIndex), resolved.lastIndexOf("\\", magicIndex));
|
|
2737
|
-
const rootBoundary = separatorIndex === 0 || separatorIndex === 2 && /^[A-Za-z]:[\\/]/.test(resolved);
|
|
2738
|
-
const prefix = rootBoundary ? resolved.slice(0, separatorIndex + 1) : separatorIndex >= 0 ? resolved.slice(0, separatorIndex) : resolved;
|
|
2739
|
-
const suffix = rootBoundary ? resolved.slice(separatorIndex + 1) : separatorIndex >= 0 ? resolved.slice(separatorIndex) : "";
|
|
2740
|
-
return `${canonicalPath(prefix)}${suffix}`;
|
|
2741
|
-
}
|
|
2742
|
-
return canonicalPath(resolved);
|
|
2743
|
-
}
|
|
2744
|
-
function validateSelectorList(value, path, kind, errorCode2 = "INVALID_SETTINGS", normalizeExtensions = true) {
|
|
2745
|
-
if (value === void 0) return void 0;
|
|
2746
|
-
if (!Array.isArray(value)) fail(errorCode2, `${path}.${kind} must be an array`);
|
|
2747
|
-
const normalized = [];
|
|
2748
|
-
for (const [index, entry] of value.entries()) {
|
|
2749
|
-
if (typeof entry !== "string" || !entry.trim()) fail(errorCode2, `${path}.${kind}[${String(index)}] must be a non-empty string`);
|
|
2750
|
-
let selector = entry.trim();
|
|
2751
|
-
if (kind === "extensions" && normalizeExtensions) {
|
|
2752
|
-
const negated = selector.startsWith("!");
|
|
2753
|
-
const body = negated ? selector.slice(1) : selector;
|
|
2754
|
-
if (!body) fail(errorCode2, `${path}.${kind}[${String(index)}] must be a valid minimatch pattern: Empty minimatch pattern ${JSON.stringify(selector)}`);
|
|
2755
|
-
try {
|
|
2756
|
-
selector = `${negated ? "!" : ""}${normalizedResourcePath(body, path)}`;
|
|
2757
|
-
} catch (error) {
|
|
2758
|
-
fail(errorCode2, `${path}.${kind}[${String(index)}] must be a valid path: ${errorText(error)}`);
|
|
2759
|
-
}
|
|
2760
|
-
}
|
|
2761
|
-
try {
|
|
2762
|
-
validateResourcePattern(selector);
|
|
2763
|
-
} catch (error) {
|
|
2764
|
-
fail(errorCode2, `${path}.${kind}[${String(index)}] must be a valid minimatch pattern: ${errorText(error)}`);
|
|
2765
|
-
}
|
|
2766
|
-
normalized.push(selector);
|
|
2767
|
-
}
|
|
2768
|
-
return Object.freeze(normalized);
|
|
2769
|
-
}
|
|
2770
|
-
function validateWorktreePostCreateCommand(value, settingsPath) {
|
|
2771
|
-
if (value === void 0) return void 0;
|
|
2772
|
-
if (!Array.isArray(value) || value.length === 0) fail("INVALID_SETTINGS", `${settingsPath}.worktreePostCreateCommand must be a non-empty array`);
|
|
2773
|
-
return Object.freeze(value.map((entry, index) => {
|
|
2774
|
-
if (typeof entry !== "string" || !entry.trim()) fail("INVALID_SETTINGS", `${settingsPath}.worktreePostCreateCommand[${String(index)}] must be a non-empty string`);
|
|
2775
|
-
return entry;
|
|
2776
|
-
}));
|
|
2777
|
-
}
|
|
2778
|
-
function selectorsFromSettings(settings) {
|
|
2779
|
-
return {
|
|
2780
|
-
...settings.skills === void 0 ? {} : { skills: settings.skills },
|
|
2781
|
-
...settings.extensions === void 0 ? {} : { extensions: settings.extensions },
|
|
2782
|
-
...settings.tools === void 0 ? {} : { tools: settings.tools }
|
|
2783
|
-
};
|
|
2784
|
-
}
|
|
2785
|
-
function selectorSet(value) {
|
|
2786
|
-
return { skills: [...value?.skills ?? []], extensions: [...value?.extensions ?? []], ...value?.tools === void 0 ? {} : { tools: [...value.tools] } };
|
|
2787
|
-
}
|
|
2788
|
-
function validateContextFileScopes(value, rolePath) {
|
|
2789
|
-
if (value === void 0) return void 0;
|
|
2790
|
-
if (!Array.isArray(value) || !value.every(isContextFileScope)) fail("INVALID_METADATA", `${rolePath}.contextFiles must be an array containing only global, project, or cwd`);
|
|
2791
|
-
return [...value];
|
|
2792
|
-
}
|
|
2793
|
-
function validateWorkflowExtensionSettings(value, settingsPath, errorCode2 = "INVALID_SETTINGS", applyDefaults = true) {
|
|
2794
|
-
if (value === void 0) return void 0;
|
|
2795
|
-
const base = `${settingsPath}.extensionSettings`;
|
|
2796
|
-
if (!object(value)) fail(errorCode2, `${base} must be an object`);
|
|
2797
|
-
const normalized = {};
|
|
2798
|
-
for (const [namespace, raw] of Object.entries(value)) {
|
|
2799
|
-
if (!validWorkflowExtensionNamespace(namespace)) fail(errorCode2, `${base} contains an invalid namespace: ${namespace}`);
|
|
2800
|
-
if (namespace === "herdr") {
|
|
2801
|
-
if (!object(raw)) fail(errorCode2, `${base}.herdr must be an object`);
|
|
2802
|
-
if (Object.keys(raw).some((key) => key !== "enableFullyInspectableMode")) fail(errorCode2, `${base}.herdr contains an unsupported setting`);
|
|
2803
|
-
if (raw.enableFullyInspectableMode !== void 0 && typeof raw.enableFullyInspectableMode !== "boolean") fail(errorCode2, `${base}.herdr.enableFullyInspectableMode must be a boolean`);
|
|
2804
|
-
normalized.herdr = Object.freeze({ ...raw.enableFullyInspectableMode === void 0 ? {} : { enableFullyInspectableMode: raw.enableFullyInspectableMode } });
|
|
2805
|
-
continue;
|
|
2806
|
-
}
|
|
2807
|
-
if (namespace === "trajectory") {
|
|
2808
|
-
if (!object(raw)) fail(errorCode2, `${base}.trajectory must be an object`);
|
|
2809
|
-
if (Object.keys(raw).some((key) => key !== "port" && key !== "themes")) fail(errorCode2, `${base}.trajectory contains an unsupported setting`);
|
|
2810
|
-
if (raw.port !== void 0 && (!positiveInteger(raw.port) || raw.port > 65535)) fail(errorCode2, `${base}.trajectory.port must be an integer from 1 to 65535`);
|
|
2811
|
-
if (raw.themes !== void 0 && typeof raw.themes !== "boolean") fail(errorCode2, `${base}.trajectory.themes must be a boolean`);
|
|
2812
|
-
normalized.trajectory = Object.freeze({ ...raw.port === void 0 ? {} : { port: raw.port }, ...raw.themes === void 0 ? applyDefaults ? { themes: false } : {} : { themes: raw.themes } });
|
|
2813
|
-
continue;
|
|
2814
|
-
}
|
|
2815
|
-
if (!jsonValue(raw)) fail(errorCode2, `${base}.${namespace} must be JSON-compatible`);
|
|
2816
|
-
normalized[namespace] = structuredClone(raw);
|
|
2817
|
-
}
|
|
2818
|
-
return deepFreeze(normalized);
|
|
2819
|
-
}
|
|
2820
|
-
function positiveRetentionInteger(value) {
|
|
2821
|
-
return positiveInteger(value) && Number.isSafeInteger(value);
|
|
2822
|
-
}
|
|
2823
|
-
function validateRetention(value, settingsPath) {
|
|
2824
|
-
if (value === void 0) return void 0;
|
|
2825
|
-
const base = `${settingsPath}.retention`;
|
|
2826
|
-
if (!object(value)) fail("INVALID_SETTINGS", `${base} must be an object`);
|
|
2827
|
-
const unknown = Object.keys(value).find((key) => key !== "olderThanDays" && key !== "maxTerminalRuns");
|
|
2828
|
-
if (unknown) fail("INVALID_SETTINGS", `Unknown retention setting at ${base}: ${unknown}`);
|
|
2829
|
-
if (value.olderThanDays !== void 0 && !positiveRetentionInteger(value.olderThanDays)) fail("INVALID_SETTINGS", `${base}.olderThanDays must be a positive integer`);
|
|
2830
|
-
if (value.maxTerminalRuns !== void 0 && !positiveRetentionInteger(value.maxTerminalRuns)) fail("INVALID_SETTINGS", `${base}.maxTerminalRuns must be a positive integer`);
|
|
2831
|
-
return Object.freeze({ ...value.olderThanDays === void 0 ? {} : { olderThanDays: value.olderThanDays }, ...value.maxTerminalRuns === void 0 ? {} : { maxTerminalRuns: value.maxTerminalRuns } });
|
|
2832
|
-
}
|
|
2833
|
-
function parseSettings(path, partial) {
|
|
2834
|
-
let parsed;
|
|
2835
|
-
try {
|
|
2836
|
-
parsed = JSON.parse(readFileSync2(path, "utf8"));
|
|
2837
|
-
} catch (error) {
|
|
2838
|
-
if (isNodeError(error, "ENOENT")) return partial ? Object.freeze({}) : DEFAULT_SETTINGS;
|
|
2839
|
-
fail("CONFIG_ERROR", `Invalid workflow settings JSON at ${path}: ${errorText(error)}`);
|
|
2840
|
-
}
|
|
2841
|
-
if (!object(parsed)) fail("INVALID_SETTINGS", `Workflow settings at ${path} must be an object`);
|
|
2842
|
-
const allowed = /* @__PURE__ */ new Set(["concurrency", "modelAliases", "skills", "extensions", "extensionSettings", "tools", "retention", "worktreePostCreateCommand", ...partial ? [] : ["backgroundWidget"]]);
|
|
2843
|
-
const unknown = Object.keys(parsed).find((key) => !allowed.has(key));
|
|
2844
|
-
if (Object.prototype.hasOwnProperty.call(parsed, "disabledAgentResources")) fail("INVALID_SETTINGS", `disabledAgentResources is no longer supported; use skills, extensions, and tools selectors (settings: ${path})`);
|
|
2845
|
-
if (unknown) fail("INVALID_SETTINGS", `Unknown workflow setting at ${path}: ${unknown}`);
|
|
2846
|
-
const concurrency = parsed.concurrency === void 0 ? partial ? void 0 : DEFAULT_SETTINGS.concurrency : parsed.concurrency;
|
|
2847
|
-
if (concurrency !== void 0 && (!positiveInteger(concurrency) || concurrency > 16)) fail("INVALID_SETTINGS", `${path}.concurrency must be an integer from 1 to 16`);
|
|
2848
|
-
const backgroundWidget = parsed.backgroundWidget === void 0 ? partial ? void 0 : DEFAULT_SETTINGS.backgroundWidget : parsed.backgroundWidget;
|
|
2849
|
-
if (backgroundWidget !== void 0 && typeof backgroundWidget !== "boolean") fail("INVALID_SETTINGS", `${path}.backgroundWidget must be a boolean`);
|
|
2850
|
-
const modelAliases = parsed.modelAliases === void 0 ? void 0 : validateModelAliases(parsed.modelAliases, path);
|
|
2851
|
-
const skills = validateSelectorList(parsed.skills, path, "skills");
|
|
2852
|
-
const tools = validateSelectorList(parsed.tools, path, "tools");
|
|
2853
|
-
const extensions = validateSelectorList(parsed.extensions, path, "extensions");
|
|
2854
|
-
const extensionSettings = parsed.extensionSettings === void 0 ? void 0 : validateWorkflowExtensionSettings(parsed.extensionSettings, path, "INVALID_SETTINGS", !partial);
|
|
2855
|
-
const retention = validateRetention(parsed.retention, path);
|
|
2856
|
-
const worktreePostCreateCommand = validateWorktreePostCreateCommand(parsed.worktreePostCreateCommand, path);
|
|
2857
|
-
return Object.freeze({
|
|
2858
|
-
...concurrency === void 0 ? {} : { concurrency },
|
|
2859
|
-
...backgroundWidget === void 0 ? {} : { backgroundWidget },
|
|
2860
|
-
...modelAliases === void 0 ? {} : { modelAliases },
|
|
2861
|
-
...skills === void 0 ? {} : { skills },
|
|
2862
|
-
...extensions === void 0 ? {} : { extensions },
|
|
2863
|
-
...extensionSettings === void 0 ? {} : { extensionSettings },
|
|
2864
|
-
...tools === void 0 ? {} : { tools },
|
|
2865
|
-
...retention === void 0 ? {} : { retention },
|
|
2866
|
-
...worktreePostCreateCommand === void 0 ? {} : { worktreePostCreateCommand }
|
|
2867
|
-
});
|
|
2868
|
-
}
|
|
2869
|
-
function loadSettings(path = workflowSettingsPath()) {
|
|
2870
|
-
return parseSettings(path, false);
|
|
2871
|
-
}
|
|
2872
|
-
function loadSettingsOverrides(path) {
|
|
2873
|
-
return parseSettings(path, true);
|
|
2874
|
-
}
|
|
2875
|
-
function resolveWorkflowSettings(cwd, projectTrusted2, globalSettingsPath = workflowSettingsPath()) {
|
|
2876
|
-
const projectSettingsPath = workflowProjectSettingsPath(cwd);
|
|
2877
|
-
const global = loadSettings(globalSettingsPath);
|
|
2878
|
-
const project = projectTrusted2 ? loadSettingsOverrides(projectSettingsPath) : Object.freeze({});
|
|
2879
|
-
const projectHas = (key) => Object.prototype.hasOwnProperty.call(project, key);
|
|
2880
|
-
const sourceFor = (key) => projectHas(key) ? projectSettingsPath : globalSettingsPath;
|
|
2881
|
-
const globalSelectors = selectorsFromSettings(global);
|
|
2882
|
-
const projectSelectors = selectorsFromSettings(project);
|
|
2883
|
-
const effectiveSelectors = selectorSet({
|
|
2884
|
-
skills: [...globalSelectors.skills ?? [], ...projectSelectors.skills ?? []],
|
|
2885
|
-
extensions: [...globalSelectors.extensions ?? [], ...projectSelectors.extensions ?? []],
|
|
2886
|
-
...globalSelectors.tools === void 0 && projectSelectors.tools === void 0 ? {} : { tools: [...globalSelectors.tools ?? [], ...projectSelectors.tools ?? []] }
|
|
2887
|
-
});
|
|
2888
|
-
const hasExtensionSelectors = global.extensions !== void 0 || project.extensions !== void 0;
|
|
2889
|
-
const extensionSettings = projectHas("extensionSettings") ? project.extensionSettings : global.extensionSettings;
|
|
2890
|
-
const sources = {
|
|
2891
|
-
concurrency: projectHas("concurrency") ? projectSettingsPath : globalSettingsPath,
|
|
2892
|
-
modelAliases: projectHas("modelAliases") ? projectSettingsPath : globalSettingsPath,
|
|
2893
|
-
skills: sourceFor("skills"),
|
|
2894
|
-
extensions: sourceFor("extensions"),
|
|
2895
|
-
tools: sourceFor("tools"),
|
|
2896
|
-
...extensionSettings === void 0 ? {} : { extensionSettings: projectHas("extensionSettings") ? projectSettingsPath : globalSettingsPath },
|
|
2897
|
-
...project.retention === void 0 && global.retention === void 0 ? {} : { retention: project.retention === void 0 ? globalSettingsPath : projectSettingsPath },
|
|
2898
|
-
...projectHas("worktreePostCreateCommand") || global.worktreePostCreateCommand !== void 0 ? { worktreePostCreateCommand: projectHas("worktreePostCreateCommand") ? projectSettingsPath : globalSettingsPath } : {}
|
|
2966
|
+
if (host.api.frozen && host.activeHosts === 0) resetWorkflowRegistry();
|
|
2967
|
+
}
|
|
2968
|
+
function retainWorkflowRegistry() {
|
|
2969
|
+
const host = workflowRegistryHost();
|
|
2970
|
+
host.activeHosts += 1;
|
|
2971
|
+
let retained = true;
|
|
2972
|
+
return () => {
|
|
2973
|
+
if (!retained) return;
|
|
2974
|
+
retained = false;
|
|
2975
|
+
host.activeHosts -= 1;
|
|
2976
|
+
if (host.activeHosts === 0) resetWorkflowRegistry();
|
|
2899
2977
|
};
|
|
2900
|
-
const effective = Object.freeze({
|
|
2901
|
-
concurrency: project.concurrency ?? global.concurrency,
|
|
2902
|
-
backgroundWidget: global.backgroundWidget ?? true,
|
|
2903
|
-
...projectHas("modelAliases") ? { modelAliases: project.modelAliases } : global.modelAliases === void 0 ? {} : { modelAliases: global.modelAliases },
|
|
2904
|
-
...effectiveSelectors.skills.length ? { skills: effectiveSelectors.skills } : global.skills !== void 0 || project.skills !== void 0 ? { skills: effectiveSelectors.skills } : {},
|
|
2905
|
-
...hasExtensionSelectors ? { extensions: effectiveSelectors.extensions } : {},
|
|
2906
|
-
...extensionSettings === void 0 ? {} : { extensionSettings },
|
|
2907
|
-
...effectiveSelectors.tools?.length ? { tools: effectiveSelectors.tools } : global.tools !== void 0 || project.tools !== void 0 ? { tools: effectiveSelectors.tools } : {},
|
|
2908
|
-
...(project.retention ?? global.retention) === void 0 ? {} : { retention: project.retention ?? global.retention },
|
|
2909
|
-
...projectHas("worktreePostCreateCommand") ? { worktreePostCreateCommand: project.worktreePostCreateCommand } : global.worktreePostCreateCommand === void 0 ? {} : { worktreePostCreateCommand: global.worktreePostCreateCommand }
|
|
2910
|
-
});
|
|
2911
|
-
return { globalSettingsPath, projectSettingsPath, projectTrusted: projectTrusted2, global, project, effective, sources };
|
|
2912
2978
|
}
|
|
2913
|
-
function
|
|
2914
|
-
|
|
2915
|
-
try {
|
|
2916
|
-
const target = modelCapability(name, aliases, knownModels, settingsPath);
|
|
2917
|
-
if (!availableModels.has(target)) unknownModel(name, target, settingsPath);
|
|
2918
|
-
} catch (error) {
|
|
2919
|
-
throw annotateModelAliasError(error, name);
|
|
2920
|
-
}
|
|
2921
|
-
}
|
|
2979
|
+
function loadingRegistry() {
|
|
2980
|
+
return workflowRegistryHost().api;
|
|
2922
2981
|
}
|
|
2923
|
-
|
|
2924
|
-
|
|
2925
|
-
|
|
2926
|
-
const project = selectorSet(selectorsFromSettings(resolved.project));
|
|
2927
|
-
const effective = selectorSet(selectorsFromSettings(resolved.effective));
|
|
2928
|
-
return { globalSettingsPath: resolved.globalSettingsPath, projectSettingsPath: resolved.projectSettingsPath, projectTrusted: projectTrusted2, global, project, effective, unmatchedSkills: [], unmatchedExtensions: [], unmatchedTools: [], selectorSources: { global: selectorsFromSettings(resolved.global), project: selectorsFromSettings(resolved.project) } };
|
|
2982
|
+
beginWorkflowExtensionLoading();
|
|
2983
|
+
function registerWorkflowExtension(extension) {
|
|
2984
|
+
loadingRegistry().register(extension);
|
|
2929
2985
|
}
|
|
2930
|
-
function
|
|
2931
|
-
|
|
2932
|
-
|
|
2933
|
-
|
|
2934
|
-
|
|
2935
|
-
|
|
2936
|
-
|
|
2937
|
-
|
|
2938
|
-
|
|
2939
|
-
|
|
2940
|
-
|
|
2941
|
-
|
|
2942
|
-
|
|
2986
|
+
function workflowCatalog(context) {
|
|
2987
|
+
return loadingRegistry().catalog(context);
|
|
2988
|
+
}
|
|
2989
|
+
function workflowCatalogIndex(context) {
|
|
2990
|
+
return loadingRegistry().catalogIndex(context);
|
|
2991
|
+
}
|
|
2992
|
+
function workflowCatalogDetail(name, context) {
|
|
2993
|
+
return loadingRegistry().catalogDetail(name, context);
|
|
2994
|
+
}
|
|
2995
|
+
function registeredWorkflowFunctions() {
|
|
2996
|
+
return loadingRegistry().functions();
|
|
2997
|
+
}
|
|
2998
|
+
function registeredWorkflowFunctionSources() {
|
|
2999
|
+
const sources = loadingRegistry().functionSources;
|
|
3000
|
+
return typeof sources === "function" ? sources() : {};
|
|
3001
|
+
}
|
|
3002
|
+
function registeredWorkflowRoleDirectories() {
|
|
3003
|
+
const directories = loadingRegistry().roleDirectories;
|
|
3004
|
+
return typeof directories === "function" ? directories() : [];
|
|
3005
|
+
}
|
|
3006
|
+
function registeredWorkflowRoleDirectoryRegistrations() {
|
|
3007
|
+
const registrations = loadingRegistry().roleDirectoryRegistrations;
|
|
3008
|
+
return typeof registrations === "function" ? registrations() : [];
|
|
2943
3009
|
}
|
|
3010
|
+
|
|
3011
|
+
// src/roles.ts
|
|
3012
|
+
var ROLE_DIRECTORY2 = "pi-extensible-workflows";
|
|
3013
|
+
var roleDefinitionPaths = /* @__PURE__ */ new WeakMap();
|
|
2944
3014
|
function parseRoleMarkdown(content, strict = false, rolePath) {
|
|
2945
3015
|
if (!strict) {
|
|
2946
3016
|
if (!content.startsWith("---\n")) return { prompt: content };
|
|
@@ -2967,7 +3037,7 @@ function parseRoleMarkdown(content, strict = false, rolePath) {
|
|
|
2967
3037
|
}
|
|
2968
3038
|
}
|
|
2969
3039
|
const rolePathValue2 = rolePath ?? "<role>";
|
|
2970
|
-
const normalizedExtensionSettings2 = validateWorkflowExtensionSettings(extensionSettings2, rolePathValue2, "INVALID_METADATA"
|
|
3040
|
+
const normalizedExtensionSettings2 = validateWorkflowExtensionSettings(extensionSettings2, rolePathValue2, "INVALID_METADATA");
|
|
2971
3041
|
const definition2 = { prompt: content.slice(end + 4).replace(/^\n/, "") };
|
|
2972
3042
|
if (meta.model) {
|
|
2973
3043
|
const model2 = unquote(meta.model);
|
|
@@ -3007,7 +3077,7 @@ function parseRoleMarkdown(content, strict = false, rolePath) {
|
|
|
3007
3077
|
const normalizedTools = validateSelectorList(tools, rolePathValue, "tools", "INVALID_METADATA");
|
|
3008
3078
|
const normalizedSkills = validateSelectorList(skills, rolePathValue, "skills", "INVALID_METADATA");
|
|
3009
3079
|
const normalizedExtensions = validateSelectorList(extensions, rolePathValue, "extensions", "INVALID_METADATA");
|
|
3010
|
-
const normalizedExtensionSettings = validateWorkflowExtensionSettings(extensionSettings, rolePathValue, "INVALID_METADATA"
|
|
3080
|
+
const normalizedExtensionSettings = validateWorkflowExtensionSettings(extensionSettings, rolePathValue, "INVALID_METADATA");
|
|
3011
3081
|
const normalizedDescription = typeof description === "string" ? description.trim() : void 0;
|
|
3012
3082
|
const normalizedModel = typeof model === "string" ? model.trim() : void 0;
|
|
3013
3083
|
if (normalizedModel !== void 0) assertModelThinking(normalizedModel, "Role model");
|
|
@@ -3022,12 +3092,11 @@ function parseRoleMarkdown(content, strict = false, rolePath) {
|
|
|
3022
3092
|
if (normalizedContextFiles !== void 0) definition.contextFiles = normalizedContextFiles;
|
|
3023
3093
|
return definition;
|
|
3024
3094
|
}
|
|
3025
|
-
|
|
3026
|
-
|
|
3027
|
-
return [join5(agentDir, ROLE_DIRECTORY, "roles")];
|
|
3095
|
+
function workflowRoleDirectories(agentDir = getAgentDir2()) {
|
|
3096
|
+
return [join6(agentDir, ROLE_DIRECTORY2, "roles")];
|
|
3028
3097
|
}
|
|
3029
3098
|
function projectRoleDirectories(root) {
|
|
3030
|
-
return [
|
|
3099
|
+
return [join6(root, ROLE_DIRECTORY2, "roles")];
|
|
3031
3100
|
}
|
|
3032
3101
|
function roleDirectorySources(dirs) {
|
|
3033
3102
|
const seen = /* @__PURE__ */ new Set();
|
|
@@ -3046,7 +3115,7 @@ function isRoleFile(dir, entry) {
|
|
|
3046
3115
|
if (entry.isFile()) return true;
|
|
3047
3116
|
if (!entry.isSymbolicLink()) return false;
|
|
3048
3117
|
try {
|
|
3049
|
-
return statSync(
|
|
3118
|
+
return statSync(join6(dir, entry.name)).isFile();
|
|
3050
3119
|
} catch (error) {
|
|
3051
3120
|
if (isNodeError(error, "ENOENT")) return false;
|
|
3052
3121
|
throw error;
|
|
@@ -3062,11 +3131,11 @@ function scanRoleFiles(dirs, extension) {
|
|
|
3062
3131
|
if (!extension && isNodeError(error, "ENOENT")) continue;
|
|
3063
3132
|
fail("INVALID_METADATA", `${roleDirectoryLabel(source, extension)} role directory "${source.path}" could not be scanned: ${errorText(error)}`);
|
|
3064
3133
|
}
|
|
3065
|
-
for (const entry of entries) if (isRoleFile(source.path, entry)) files.push({ name: basename3(entry.name, ".md"), path:
|
|
3134
|
+
for (const entry of entries) if (isRoleFile(source.path, entry)) files.push({ name: basename3(entry.name, ".md"), path: join6(source.path, entry.name), source });
|
|
3066
3135
|
}
|
|
3067
3136
|
return files.sort((left, right) => left.name.localeCompare(right.name) || left.path.localeCompare(right.path));
|
|
3068
3137
|
}
|
|
3069
|
-
function
|
|
3138
|
+
function roleEntries(dirs, extension) {
|
|
3070
3139
|
const files = scanRoleFiles(dirs, extension);
|
|
3071
3140
|
const starterFiles = files.filter(({ source }) => source.builtin === true);
|
|
3072
3141
|
const regularFiles = files.filter(({ source }) => source.builtin !== true);
|
|
@@ -3077,7 +3146,9 @@ function readRoleDefinitions(dirs, extension = false) {
|
|
|
3077
3146
|
}
|
|
3078
3147
|
const read = (roleFiles) => Object.fromEntries(roleFiles.map(({ name, path, source }) => {
|
|
3079
3148
|
try {
|
|
3080
|
-
|
|
3149
|
+
const definition = parseRoleMarkdown(readFileSync3(path, "utf8"), true, path);
|
|
3150
|
+
roleDefinitionPaths.set(definition, path);
|
|
3151
|
+
return [name, { name, path, source, definition }];
|
|
3081
3152
|
} catch (error) {
|
|
3082
3153
|
if (extension) fail("INVALID_METADATA", `${roleDirectoryLabel(source)} role directory "${source.path}" contains invalid role "${name}" at "${path}": ${errorText(error)}`);
|
|
3083
3154
|
throw error;
|
|
@@ -3085,11 +3156,127 @@ function readRoleDefinitions(dirs, extension = false) {
|
|
|
3085
3156
|
}));
|
|
3086
3157
|
return extension ? { ...read(starterFiles), ...read(regularFiles) } : read(files);
|
|
3087
3158
|
}
|
|
3159
|
+
function discoveryInput(input) {
|
|
3160
|
+
return { cwd: input.cwd, agentDir: input.agentDir ?? getAgentDir2(), projectTrusted: input.projectTrusted ?? true, extensionRoleDirectories: input.extensionRoleDirectories ?? registeredWorkflowRoleDirectoryRegistrations() };
|
|
3161
|
+
}
|
|
3162
|
+
function discoveredRoleEntries(input) {
|
|
3163
|
+
const resolved = discoveryInput(input);
|
|
3164
|
+
return {
|
|
3165
|
+
...roleEntries(resolved.extensionRoleDirectories, true),
|
|
3166
|
+
...roleEntries(workflowRoleDirectories(resolved.agentDir), false),
|
|
3167
|
+
...resolved.projectTrusted ? roleEntries(projectRoleDirectories(join6(resolved.cwd, ".pi")), false) : {}
|
|
3168
|
+
};
|
|
3169
|
+
}
|
|
3088
3170
|
function loadProjectAgentDefinitions(cwd) {
|
|
3089
|
-
return
|
|
3171
|
+
return deepFreeze(Object.fromEntries(Object.entries(roleEntries(projectRoleDirectories(join6(cwd, ".pi")), false)).map(([name, entry]) => [name, entry.definition])));
|
|
3172
|
+
}
|
|
3173
|
+
function discoverRoles(input) {
|
|
3174
|
+
return deepFreeze(Object.fromEntries(Object.entries(discoveredRoleEntries(input)).map(([name, entry]) => [name, entry.definition])));
|
|
3175
|
+
}
|
|
3176
|
+
function loadRole(name, input) {
|
|
3177
|
+
const entry = discoveredRoleEntries(input)[name];
|
|
3178
|
+
if (!entry) fail("UNKNOWN_AGENT_TYPE", `Unknown agent role: ${name}`);
|
|
3179
|
+
return entry.definition;
|
|
3180
|
+
}
|
|
3181
|
+
function loadAgentDefinitions(cwd, agentDir = getAgentDir2(), projectTrusted2 = true, extensionRoleDirectories = registeredWorkflowRoleDirectoryRegistrations()) {
|
|
3182
|
+
return discoverRoles({ cwd, agentDir, projectTrusted: projectTrusted2, extensionRoleDirectories });
|
|
3183
|
+
}
|
|
3184
|
+
function canonicalExtensionSelector(selector, base = process.cwd()) {
|
|
3185
|
+
const negated = selector.startsWith("!");
|
|
3186
|
+
const body = negated ? selector.slice(1) : selector;
|
|
3187
|
+
if (body === "*" || body === "**" || body.startsWith("**/") || body.startsWith("builtin:")) return selector;
|
|
3188
|
+
const resolved = resolve4(base, body);
|
|
3189
|
+
if (resourcePatternHasMagic(body)) return `${negated ? "!" : ""}${resolved}`;
|
|
3190
|
+
return `${negated ? "!" : ""}${canonicalPath(resolved)}`;
|
|
3191
|
+
}
|
|
3192
|
+
function selectorsFromSettings2(settings) {
|
|
3193
|
+
return { ...settings.skills === void 0 ? {} : { skills: settings.skills }, ...settings.extensions === void 0 ? {} : { extensions: settings.extensions }, ...settings.tools === void 0 ? {} : { tools: settings.tools } };
|
|
3194
|
+
}
|
|
3195
|
+
function rootToolNames(value) {
|
|
3196
|
+
return value === void 0 ? void 0 : value instanceof Set ? [...value] : [...value];
|
|
3197
|
+
}
|
|
3198
|
+
function roleDefinitionFor(name, options) {
|
|
3199
|
+
if (name === void 0) return {};
|
|
3200
|
+
if (options.definition !== void 0) {
|
|
3201
|
+
const path = roleDefinitionPaths.get(options.definition);
|
|
3202
|
+
return { definition: options.definition, ...path === void 0 ? {} : { path } };
|
|
3203
|
+
}
|
|
3204
|
+
if (options.definitions?.[name] !== void 0) {
|
|
3205
|
+
const definition = options.definitions[name];
|
|
3206
|
+
const path = roleDefinitionPaths.get(definition);
|
|
3207
|
+
return { definition, ...path === void 0 ? {} : { path } };
|
|
3208
|
+
}
|
|
3209
|
+
const entries = discoveredRoleEntries(options);
|
|
3210
|
+
const entry = entries[name];
|
|
3211
|
+
if (!entry) fail("UNKNOWN_AGENT_TYPE", `Unknown agent role: ${name}`);
|
|
3212
|
+
return entry;
|
|
3213
|
+
}
|
|
3214
|
+
function resolveRole(name, options) {
|
|
3215
|
+
const { definition, path } = roleDefinitionFor(name, options);
|
|
3216
|
+
const settings = options.selectorSources === void 0 ? resolveWorkflowSettings(options.cwd, options.projectTrusted ?? true, options.settingsPath ?? workflowSettingsPath(options.agentDir ?? getAgentDir2())) : void 0;
|
|
3217
|
+
const baseSources = options.selectorSources ?? { global: selectorsFromSettings2(settings?.global ?? {}), project: selectorsFromSettings2(settings?.project ?? {}) };
|
|
3218
|
+
const canonicalSources = (sources) => ({
|
|
3219
|
+
...sources,
|
|
3220
|
+
global: { ...sources.global, ...sources.global.extensions === void 0 ? {} : { extensions: sources.global.extensions.map((selector) => canonicalExtensionSelector(selector, options.cwd)) } },
|
|
3221
|
+
project: { ...sources.project, ...sources.project.extensions === void 0 ? {} : { extensions: sources.project.extensions.map((selector) => canonicalExtensionSelector(selector, options.cwd)) } },
|
|
3222
|
+
...sources.role === void 0 ? {} : { role: { ...sources.role, ...sources.role.extensions === void 0 ? {} : { extensions: sources.role.extensions.map((selector) => canonicalExtensionSelector(selector, options.cwd)) } } },
|
|
3223
|
+
...sources.call === void 0 ? {} : { call: { ...sources.call, ...sources.call.extensions === void 0 ? {} : { extensions: sources.call.extensions.map((selector) => canonicalExtensionSelector(selector, options.cwd)) } } }
|
|
3224
|
+
});
|
|
3225
|
+
const roleLayer = definition?.skills !== void 0 || definition?.extensions !== void 0 || definition?.tools !== void 0 ? { ...definition.skills === void 0 ? {} : { skills: definition.skills }, ...definition.extensions === void 0 ? {} : { extensions: definition.extensions.map((selector) => canonicalExtensionSelector(selector, path ? dirname4(path) : options.cwd)) }, ...definition.tools === void 0 ? {} : { tools: definition.tools } } : baseSources.role;
|
|
3226
|
+
const callLayer = options.skills !== void 0 || options.extensions !== void 0 || options.tools !== void 0 ? { ...options.skills === void 0 ? {} : { skills: options.skills }, ...options.extensions === void 0 ? {} : { extensions: options.extensions.map((selector) => canonicalExtensionSelector(selector, options.cwd)) }, ...options.tools === void 0 ? {} : { tools: options.tools } } : baseSources.call;
|
|
3227
|
+
const selectorSources = canonicalSources({ global: baseSources.global, project: baseSources.project, ...roleLayer === void 0 ? {} : { role: roleLayer }, ...callLayer === void 0 ? {} : { call: callLayer } });
|
|
3228
|
+
const callSelectors = selectorSources.call;
|
|
3229
|
+
const roleSelectors = selectorSources.role;
|
|
3230
|
+
const selectorLayers = {
|
|
3231
|
+
skills: [selectorSources.global.skills, selectorSources.project.skills, roleSelectors?.skills, callSelectors?.skills],
|
|
3232
|
+
extensions: [selectorSources.global.extensions, selectorSources.project.extensions, roleSelectors?.extensions, callSelectors?.extensions],
|
|
3233
|
+
tools: [selectorSources.global.tools, selectorSources.project.tools, roleSelectors?.tools, callSelectors?.tools]
|
|
3234
|
+
};
|
|
3235
|
+
const resources = options.resources ?? {};
|
|
3236
|
+
const candidateTools = options.inheritedTools ?? rootToolNames(options.rootTools) ?? resources.tools;
|
|
3237
|
+
const tools = options.effectiveTools ?? (candidateTools === void 0 ? void 0 : selectResourcesByLayers(selectorLayers.tools, candidateTools));
|
|
3238
|
+
const rootTools = rootToolNames(options.rootTools);
|
|
3239
|
+
if (rootTools !== void 0) {
|
|
3240
|
+
const outsideEffectiveTool = options.effectiveTools?.find((tool) => !candidateTools?.includes(tool));
|
|
3241
|
+
if (outsideEffectiveTool) fail("UNKNOWN_TOOL", `Tool is outside the launching session boundary: ${outsideEffectiveTool}`);
|
|
3242
|
+
const outsideCallTool = (options.tools ?? []).find((tool) => !tool.startsWith("!") && !resourcePatternHasMagic(tool) && !candidateTools?.includes(tool));
|
|
3243
|
+
if (outsideCallTool) fail("UNKNOWN_TOOL", `Tool is outside the launching session boundary: ${outsideCallTool}`);
|
|
3244
|
+
const outsideRootTool = tools?.find((tool) => !rootTools.includes(tool));
|
|
3245
|
+
if (outsideRootTool) fail("UNKNOWN_TOOL", `Tool is outside the launching session boundary: ${outsideRootTool}`);
|
|
3246
|
+
}
|
|
3247
|
+
const selectedSkills = resources.skills === void 0 ? void 0 : selectResourcesByLayers(selectorLayers.skills, resources.skills);
|
|
3248
|
+
const extensionResources = resources.extensions?.map((extension) => extensionIdentity(extension));
|
|
3249
|
+
const selectedExtensions = extensionResources === void 0 ? void 0 : selectResourcesByLayers(selectorLayers.extensions, extensionResources);
|
|
3250
|
+
const unmatchedSkills = resources.skills === void 0 ? void 0 : unmatchedResourcePatterns(selectorLayers.skills.flatMap((layer) => layer ?? []), resources.skills);
|
|
3251
|
+
const unmatchedExtensions = extensionResources === void 0 ? void 0 : unmatchedResourcePatterns(selectorLayers.extensions.flatMap((layer) => layer ?? []), extensionResources);
|
|
3252
|
+
const unmatchedTools = candidateTools === void 0 ? void 0 : unmatchedResourcePatterns(selectorLayers.tools.flatMap((layer) => layer ?? []), candidateTools);
|
|
3253
|
+
const aliases = options.modelAliases ?? settings?.effective.modelAliases ?? {};
|
|
3254
|
+
const requestedModel = options.model ?? (definition?.model && definition.thinking && !definition.model.includes(":") ? `${definition.model}:${definition.thinking}` : definition?.model);
|
|
3255
|
+
const blockedAlias = requestedModel?.split(":", 1)[0];
|
|
3256
|
+
if (requestedModel !== void 0 && blockedAlias && options.blockedAliases?.has(blockedAlias) && !modelAliasName(requestedModel, aliases)) {
|
|
3257
|
+
const target = options.blockedAliasTargets?.[blockedAlias];
|
|
3258
|
+
fail("UNKNOWN_MODEL", `Unknown model alias ${requestedModel}${target ? ` resolved to ${target}` : ""}${options.settingsPath ? ` (settings: ${options.settingsPath})` : ""}`);
|
|
3259
|
+
}
|
|
3260
|
+
let model = options.modelOverride;
|
|
3261
|
+
if (model === void 0 && requestedModel !== void 0) {
|
|
3262
|
+
assertModelThinking(requestedModel);
|
|
3263
|
+
model = resolveModelReference(requestedModel, aliases, options.knownModels ?? options.availableModels, options.settingsPath ?? settings?.sources.modelAliases);
|
|
3264
|
+
}
|
|
3265
|
+
if (model === void 0) model = options.rootModel;
|
|
3266
|
+
const availableModels = options.knownModels ?? options.availableModels ?? (options.rootModel === void 0 ? void 0 : /* @__PURE__ */ new Set([modelCapability(options.rootModel)]));
|
|
3267
|
+
if (model !== void 0 && availableModels !== void 0 && !availableModels.has(modelCapability(model))) fail("UNKNOWN_MODEL", `Unknown model${requestedModel ? ` ${requestedModel} resolved to ${modelCapability(model)}` : ""}${options.settingsPath ? ` (settings: ${options.settingsPath})` : ""}`);
|
|
3268
|
+
const prompt = definition?.prompt ?? "";
|
|
3269
|
+
const overrideSystemPrompt = definition?.overrideSystemPrompt === true;
|
|
3270
|
+
const contextFiles = options.contextFiles ?? definition?.contextFiles;
|
|
3271
|
+
return { ...name === void 0 ? {} : { name }, ...definition === void 0 ? {} : { definition }, ...model === void 0 ? {} : { model }, ...modelAliasName(requestedModel ?? "", aliases) && requestedModel ? { requestedModel } : {}, ...tools === void 0 ? {} : { tools }, prompt, overrideSystemPrompt, systemPrompt: { mode: overrideSystemPrompt ? "override" : "append", text: prompt }, ...contextFiles === void 0 ? {} : { contextFiles: [...contextFiles] }, ...definition?.extensionSettings === void 0 ? {} : { extensionSettings: definition.extensionSettings }, selectorSources, selectorLayers, ...selectedSkills === void 0 ? {} : { selectedSkills }, ...selectedExtensions === void 0 ? {} : { selectedExtensions }, ...unmatchedSkills === void 0 ? {} : { unmatchedSkills }, ...unmatchedExtensions === void 0 ? {} : { unmatchedExtensions }, ...unmatchedTools === void 0 ? {} : { unmatchedTools } };
|
|
3090
3272
|
}
|
|
3091
|
-
|
|
3092
|
-
|
|
3273
|
+
|
|
3274
|
+
// src/validation.ts
|
|
3275
|
+
function validateCheckpoint(value) {
|
|
3276
|
+
if (!object(value) || Object.keys(value).some((key) => !["name", "prompt", "context"].includes(key)) || typeof value.name !== "string" || value.name.trim() === "" || typeof value.prompt !== "string" || !jsonValue(value.context)) fail("INVALID_METADATA", "checkpoint requires only name, prompt, and JSON context");
|
|
3277
|
+
if (Buffer.byteLength(value.prompt) > 1024) fail("INVALID_METADATA", "checkpoint prompt exceeds 1024 UTF-8 bytes");
|
|
3278
|
+
if (Buffer.byteLength(JSON.stringify(value.context)) > 4096) fail("INVALID_METADATA", "checkpoint context exceeds 4096 UTF-8 bytes");
|
|
3279
|
+
return { name: value.name, prompt: value.prompt, context: value.context };
|
|
3093
3280
|
}
|
|
3094
3281
|
function validateRolePolicies(definitions, roles, availableModels, aliases = {}, knownModels = availableModels, settingsPath) {
|
|
3095
3282
|
for (const role of roles) {
|
|
@@ -3278,6 +3465,19 @@ function callHasTrailingComma(source, call) {
|
|
|
3278
3465
|
}
|
|
3279
3466
|
return current?.type.label === ")" && previous?.type.label === ",";
|
|
3280
3467
|
}
|
|
3468
|
+
function scriptToolReferences(script) {
|
|
3469
|
+
const body = workflowBody(script);
|
|
3470
|
+
if (!body.trim()) return [];
|
|
3471
|
+
const names = /* @__PURE__ */ new Set();
|
|
3472
|
+
const opaque = (node, parent) => {
|
|
3473
|
+
if (node.type === "Identifier" && node.name === "tools") {
|
|
3474
|
+
if (parent?.type === "MemberExpression" && parent.object === node && !parent.computed && parent.property.type === "Identifier") names.add(parent.property.name);
|
|
3475
|
+
else if (!(parent?.type === "Property" && parent.key === node && !parent.computed && !parent.shorthand || parent?.type === "MemberExpression" && parent.property === node && !parent.computed)) return true;
|
|
3476
|
+
}
|
|
3477
|
+
return astChildren(node).some((child) => opaque(child, node));
|
|
3478
|
+
};
|
|
3479
|
+
return opaque(parseWorkflow(body)) ? void 0 : [...names];
|
|
3480
|
+
}
|
|
3281
3481
|
function instrumentWorkflow(script) {
|
|
3282
3482
|
const body = workflowBody(script);
|
|
3283
3483
|
if (!body.trim()) return body;
|
|
@@ -3346,12 +3546,6 @@ function workflowPrompt(template, values) {
|
|
|
3346
3546
|
return typeof value === "string" ? value : JSON.stringify(value, null, 2);
|
|
3347
3547
|
});
|
|
3348
3548
|
}
|
|
3349
|
-
function validateSchema(schema, at = "schema") {
|
|
3350
|
-
if (!object(schema) || Object.getPrototypeOf(schema) !== Object.prototype || !jsonValue(schema)) fail("INVALID_SCHEMA", `${at} must be a plain JSON-compatible Schema object`);
|
|
3351
|
-
if (typeof schema.type !== "string" && !Array.isArray(schema.type) && schema.$ref === void 0 && schema.anyOf === void 0 && schema.oneOf === void 0 && schema.allOf === void 0 && schema.const === void 0 && schema.enum === void 0) fail("INVALID_SCHEMA", `${at} has no JSON Schema shape`);
|
|
3352
|
-
if (schema.required !== void 0 && (!Array.isArray(schema.required) || schema.required.some((key) => typeof key !== "string"))) fail("INVALID_SCHEMA", `${at}.required must be an array of strings`);
|
|
3353
|
-
if (schema.properties !== void 0 && !object(schema.properties)) fail("INVALID_SCHEMA", `${at}.properties must be an object`);
|
|
3354
|
-
}
|
|
3355
3549
|
var AGENT_OPTION_KEYS = /* @__PURE__ */ new Set(["label", "model", "tools", "skills", "extensions", "contextFiles", "role", "outputSchema", "retries", "timeoutMs"]);
|
|
3356
3550
|
function validateAgentOption(key, value, aliases, knownModels, settingsPath) {
|
|
3357
3551
|
switch (key) {
|
|
@@ -3597,7 +3791,7 @@ function validateWorkflowLaunchWithRegistry(params, context, registry) {
|
|
|
3597
3791
|
let fileScript;
|
|
3598
3792
|
if (scriptPath !== void 0) {
|
|
3599
3793
|
try {
|
|
3600
|
-
fileScript =
|
|
3794
|
+
fileScript = readFileSync4(resolve5(context.cwd, scriptPath), "utf8");
|
|
3601
3795
|
} catch (error) {
|
|
3602
3796
|
fail("INVALID_SYNTAX", `Cannot read workflow script file ${scriptPath}: ${errorText(error)}`);
|
|
3603
3797
|
}
|
|
@@ -3631,7 +3825,7 @@ function validateWorkflowLaunchWithRegistry(params, context, registry) {
|
|
|
3631
3825
|
import { fork, spawn } from "node:child_process";
|
|
3632
3826
|
import { mkdtempSync, realpathSync as realpathSync2, rmSync as rmSync2, writeFileSync as writeFileSync2 } from "node:fs";
|
|
3633
3827
|
import { tmpdir } from "node:os";
|
|
3634
|
-
import { join as
|
|
3828
|
+
import { join as join7 } from "node:path";
|
|
3635
3829
|
import { StringDecoder } from "node:string_decoder";
|
|
3636
3830
|
var RPC_LIMIT_BYTES = 10 * 1024 * 1024;
|
|
3637
3831
|
var HEARTBEAT_TIMEOUT_MS = 5e3;
|
|
@@ -3889,6 +4083,30 @@ const functions = Object.freeze(Object.fromEntries(Object.entries(config.functio
|
|
|
3889
4083
|
Object.defineProperty(result, "toJSON", { value() { throw workError("INVALID_METADATA", "Workflow function result is a Promise; await it before serialization"); } });
|
|
3890
4084
|
return result;
|
|
3891
4085
|
}])));
|
|
4086
|
+
// tools.<id>(args) runs a Pi session tool through the host. Any identifier resolves, so a resumed run
|
|
4087
|
+
// replays journaled calls even when the tool is gone; the host rejects identifiers it cannot call.
|
|
4088
|
+
// A call is the n-th call with the same arguments in its scope, so replay does not depend on the
|
|
4089
|
+
// order in which concurrent calls start.
|
|
4090
|
+
const NOT_TOOLS = new Set(["then", "toJSON", "toString", "valueOf", "constructor"]);
|
|
4091
|
+
const sortedJson = value => Array.isArray(value) ? value.map(sortedJson) : value && typeof value === "object" ? Object.fromEntries(Object.keys(value).sort().map(key => [key, sortedJson(value[key])])) : value;
|
|
4092
|
+
const toolOccurrences = new Map();
|
|
4093
|
+
const toolCall = id => (...values) => {
|
|
4094
|
+
if (worktreeOwners.getStore()) throw workError("INVALID_METADATA", "tools." + id + " is unavailable inside withWorktree: session tools resolve paths against the session working directory");
|
|
4095
|
+
if (values.length > 1 || values[0] !== undefined && (!values[0] || typeof values[0] !== "object" || Array.isArray(values[0]))) throw workError("INVALID_METADATA", "tools." + id + " takes one arguments object");
|
|
4096
|
+
const args = values[0] === undefined ? {} : values[0];
|
|
4097
|
+
const structuralPath = inheritedAgentPath.getStore() || [];
|
|
4098
|
+
const key = JSON.stringify([structuralPath, id, sortedJson(args)]);
|
|
4099
|
+
const occurrence = (toolOccurrences.get(key) || 0) + 1;
|
|
4100
|
+
toolOccurrences.set(key, occurrence);
|
|
4101
|
+
return guardedResult(rpc("tool", [id, args, { structuralPath: [...structuralPath], occurrence }]), "tool");
|
|
4102
|
+
};
|
|
4103
|
+
const tools = new Proxy(Object.create(null), {
|
|
4104
|
+
get: (_target, key) => typeof key === "string" && !NOT_TOOLS.has(key) ? toolCall(key) : undefined,
|
|
4105
|
+
// Probing would branch on the session that resumed the run; the launch already checks named tools.
|
|
4106
|
+
has: () => { throw workError("INVALID_METADATA", "Workflow scripts cannot probe tools with 'in'; call tools.<name>(...) directly, and the launch checks that the tool exists"); },
|
|
4107
|
+
set: () => false,
|
|
4108
|
+
defineProperty: () => false,
|
|
4109
|
+
});
|
|
3892
4110
|
const recordEntries = (value, kind) => {
|
|
3893
4111
|
if (!value || typeof value !== "object" || Array.isArray(value)) throw workError("INVALID_METADATA", kind + " must be a record");
|
|
3894
4112
|
return Object.entries(value);
|
|
@@ -3949,7 +4167,7 @@ const pipeline = async (operationName, items, stages) => {
|
|
|
3949
4167
|
return Object.fromEntries(results.map(result => [result.name, result.value]));
|
|
3950
4168
|
};
|
|
3951
4169
|
const safeMath = Object.fromEntries(Object.getOwnPropertyNames(Math).filter(name => name !== "random").map(name => [name, Math[name]]));
|
|
3952
|
-
const sandbox = { agent, shell, withWorktree: rejectWorktree, prompt, checkpoint, parallel, pipeline, phase, log, args: config.args, Promise, JSON, Math: Object.freeze(safeMath) };
|
|
4170
|
+
const sandbox = { agent, shell, withWorktree: rejectWorktree, prompt, checkpoint, parallel, pipeline, phase, log, tools, args: config.args, Promise, JSON, Math: Object.freeze(safeMath) };
|
|
3953
4171
|
for (const [name, fn] of Object.entries(functions)) Object.defineProperty(sandbox, name, { value: fn, writable: false, configurable: false });
|
|
3954
4172
|
for (const name of ["Date","eval","Function","WebAssembly","process","require","module","exports","console","fetch","XMLHttpRequest","WebSocket","performance","crypto","setTimeout","setInterval","setImmediate","queueMicrotask","Intl","SharedArrayBuffer","Atomics"]) sandbox[name] = undefined;
|
|
3955
4173
|
const context = vm.createContext(sandbox, { codeGeneration: { strings: false, wasm: false } });
|
|
@@ -3993,6 +4211,13 @@ function readShellIdentity(value) {
|
|
|
3993
4211
|
const identity = readAgentIdentity(value);
|
|
3994
4212
|
return { structuralPath: identity.structuralPath, callSite: identity.callSite, occurrence: identity.occurrence, ...identity.worktreeOwner ? { worktreeOwner: identity.worktreeOwner } : {} };
|
|
3995
4213
|
}
|
|
4214
|
+
function readToolIdentity(value) {
|
|
4215
|
+
if (!object(value) || !Array.isArray(value.structuralPath) || !value.structuralPath.every((part) => typeof part === "string" && Boolean(part.trim())) || !positiveInteger(value.occurrence)) fail("INTERNAL_ERROR", "Invalid workflow tool identity");
|
|
4216
|
+
return { structuralPath: [...value.structuralPath], occurrence: value.occurrence };
|
|
4217
|
+
}
|
|
4218
|
+
function toolIdentityPath(identifier, args, identity) {
|
|
4219
|
+
return structuralPath("tool", ...identity.structuralPath, identifier, `args:${jsonDigest(args)}`, String(identity.occurrence));
|
|
4220
|
+
}
|
|
3996
4221
|
function agentHandleTurnPath(handle, turn) {
|
|
3997
4222
|
return structuralPath("agent", "handle", handle, `turn:${String(turn)}`);
|
|
3998
4223
|
}
|
|
@@ -4037,7 +4262,7 @@ function shellProcessKill(child) {
|
|
|
4037
4262
|
forceKill.unref();
|
|
4038
4263
|
}
|
|
4039
4264
|
function executeShellCommand(command, options, signal, cwd = process.cwd()) {
|
|
4040
|
-
return new Promise((
|
|
4265
|
+
return new Promise((resolve9, reject) => {
|
|
4041
4266
|
let child;
|
|
4042
4267
|
try {
|
|
4043
4268
|
child = spawn(command, { shell: true, cwd, env: { ...process.env, ...options.env ?? {} }, detached: process.platform !== "win32", stdio: ["ignore", "pipe", "pipe"] });
|
|
@@ -4107,7 +4332,7 @@ function executeShellCommand(command, options, signal, cwd = process.cwd()) {
|
|
|
4107
4332
|
reject(error instanceof WorkflowError ? error : new WorkflowError("RPC_LIMIT_EXCEEDED", errorText(error)));
|
|
4108
4333
|
return;
|
|
4109
4334
|
}
|
|
4110
|
-
|
|
4335
|
+
resolve9(result);
|
|
4111
4336
|
});
|
|
4112
4337
|
if (signal.aborted) {
|
|
4113
4338
|
onAbort();
|
|
@@ -4131,8 +4356,8 @@ function workflowErrorFromWorker(error) {
|
|
|
4131
4356
|
function runWorkflow(script, args = null, bridge = {}, signal) {
|
|
4132
4357
|
encoded(args);
|
|
4133
4358
|
const config = JSON.stringify({ script: instrumentWorkflow(script), args: structuredClone(args), functions: bridge.functions ?? {} });
|
|
4134
|
-
const childDir = realpathSync2(mkdtempSync(
|
|
4135
|
-
const childFile =
|
|
4359
|
+
const childDir = realpathSync2(mkdtempSync(join7(tmpdir(), "pi-wf-")));
|
|
4360
|
+
const childFile = join7(childDir, "child.cjs");
|
|
4136
4361
|
writeFileSync2(childFile, childSource);
|
|
4137
4362
|
const child = fork(childFile, [String(RPC_LIMIT_BYTES), config], {
|
|
4138
4363
|
execArgv: (() => {
|
|
@@ -4172,7 +4397,7 @@ function runWorkflow(script, args = null, bridge = {}, signal) {
|
|
|
4172
4397
|
}
|
|
4173
4398
|
if (now - lastHeartbeatAt >= HEARTBEAT_TIMEOUT_MS) stop("WORKER_UNRESPONSIVE", "Workflow worker missed its five-second heartbeat");
|
|
4174
4399
|
}, HEARTBEAT_INTERVAL_MS);
|
|
4175
|
-
const result = new Promise((
|
|
4400
|
+
const result = new Promise((resolve9, reject) => {
|
|
4176
4401
|
rejectResult = reject;
|
|
4177
4402
|
child.on("message", (raw) => {
|
|
4178
4403
|
try {
|
|
@@ -4187,7 +4412,7 @@ function runWorkflow(script, args = null, bridge = {}, signal) {
|
|
|
4187
4412
|
if (message.type === "result") {
|
|
4188
4413
|
encoded(message.value);
|
|
4189
4414
|
finish();
|
|
4190
|
-
|
|
4415
|
+
resolve9(message.value);
|
|
4191
4416
|
return;
|
|
4192
4417
|
}
|
|
4193
4418
|
if (message.type === "error") {
|
|
@@ -4285,6 +4510,11 @@ function runWorkflow(script, args = null, bridge = {}, signal) {
|
|
|
4285
4510
|
if (!OUTCOME_ERRORS.has(typed.code)) throw typed;
|
|
4286
4511
|
value = branded({ name, ok: false, failedAt: name, error: workerErrorShape(typed) });
|
|
4287
4512
|
}
|
|
4513
|
+
} else if (method === "tool") {
|
|
4514
|
+
if (typeof values[0] !== "string" || !object(values[1])) fail("INTERNAL_ERROR", "tool requires an identifier and object arguments");
|
|
4515
|
+
const identity = readToolIdentity(values[2]);
|
|
4516
|
+
if (!bridge.tool) fail("UNKNOWN_TOOL", `tools.${values[0]} is unavailable: this run has no session tools`);
|
|
4517
|
+
value = await bridge.tool(values[0], values[1], controller.signal, identity);
|
|
4288
4518
|
} else if (method === "worktree") {
|
|
4289
4519
|
if (!bridge.worktree || typeof values[0] !== "string" || !values[0]) fail("INTERNAL_ERROR", "worktree requires an active host bridge and scope");
|
|
4290
4520
|
value = await bridge.worktree(values[0], controller.signal);
|
|
@@ -4343,20 +4573,20 @@ async function persistAgentAttempts(store, id, attempts) {
|
|
|
4343
4573
|
import { randomUUID as randomUUID5 } from "node:crypto";
|
|
4344
4574
|
import { existsSync as existsSync2 } from "node:fs";
|
|
4345
4575
|
import { copyFile as copyFile2, mkdir as mkdir4 } from "node:fs/promises";
|
|
4346
|
-
import { dirname as
|
|
4576
|
+
import { dirname as dirname8, join as join16 } from "node:path";
|
|
4347
4577
|
import { fileURLToPath as fileURLToPath5 } from "node:url";
|
|
4348
4578
|
import { Type as Type3 } from "@earendil-works/pi-ai";
|
|
4349
|
-
import { copyToClipboard, getAgentDir as
|
|
4579
|
+
import { copyToClipboard, getAgentDir as getAgentDir5, ModelSelectorComponent } from "@earendil-works/pi-coding-agent";
|
|
4350
4580
|
|
|
4351
4581
|
// src/agent-execution.ts
|
|
4352
|
-
import { existsSync, realpathSync as realpathSync3, readFileSync as
|
|
4582
|
+
import { existsSync, realpathSync as realpathSync3, readFileSync as readFileSync5 } from "node:fs";
|
|
4353
4583
|
import { copyFile } from "node:fs/promises";
|
|
4354
4584
|
import { homedir as homedir5 } from "node:os";
|
|
4355
|
-
import { basename as basename4, dirname as
|
|
4585
|
+
import { basename as basename4, dirname as dirname5, join as join8, resolve as resolve6 } from "node:path";
|
|
4356
4586
|
import { fileURLToPath as fileURLToPath3 } from "node:url";
|
|
4357
4587
|
import { Type } from "@earendil-works/pi-ai";
|
|
4358
4588
|
import { Compile } from "typebox/compile";
|
|
4359
|
-
import { createAgentSession, DefaultPackageManager, DefaultResourceLoader, defineTool, getAgentDir as
|
|
4589
|
+
import { createAgentSession, createCodemodeExtension, createMcpExtension, createToolSearchExtension, DefaultPackageManager, DefaultResourceLoader, defineTool, getAgentDir as getAgentDir3, ModelRuntime, SessionManager, SettingsManager } from "@earendil-works/pi-coding-agent";
|
|
4360
4590
|
|
|
4361
4591
|
// src/pi-runtime-adapter.ts
|
|
4362
4592
|
var TURN_START_EVENTS = /* @__PURE__ */ new Set(["turn_start", "turn_started", "turnStarted"]);
|
|
@@ -4631,8 +4861,8 @@ function createLiveSessionHandoff() {
|
|
|
4631
4861
|
let request;
|
|
4632
4862
|
const waitForBoundary = () => {
|
|
4633
4863
|
if (!turnActive) return Promise.resolve();
|
|
4634
|
-
boundary ??= new Promise((
|
|
4635
|
-
resolveBoundary =
|
|
4864
|
+
boundary ??= new Promise((resolve9) => {
|
|
4865
|
+
resolveBoundary = resolve9;
|
|
4636
4866
|
});
|
|
4637
4867
|
return boundary;
|
|
4638
4868
|
};
|
|
@@ -4664,11 +4894,11 @@ function createLiveSessionHandoff() {
|
|
|
4664
4894
|
if (request) return request;
|
|
4665
4895
|
if (isReleased()) return;
|
|
4666
4896
|
state = "handoff-pending";
|
|
4667
|
-
takeover = new Promise((
|
|
4668
|
-
resolveTakeover =
|
|
4897
|
+
takeover = new Promise((resolve9) => {
|
|
4898
|
+
resolveTakeover = resolve9;
|
|
4669
4899
|
});
|
|
4670
|
-
resume = new Promise((
|
|
4671
|
-
resolveResume =
|
|
4900
|
+
resume = new Promise((resolve9) => {
|
|
4901
|
+
resolveResume = resolve9;
|
|
4672
4902
|
});
|
|
4673
4903
|
request = (async () => {
|
|
4674
4904
|
await waitForBoundary();
|
|
@@ -5195,36 +5425,37 @@ function parseModel(value, fallback, aliases = {}, knownModels, settingsPath) {
|
|
|
5195
5425
|
function accounting(stats) {
|
|
5196
5426
|
return { input: stats.tokens.input, output: stats.tokens.output, cacheRead: stats.tokens.cacheRead, cacheWrite: stats.tokens.cacheWrite, cost: stats.cost };
|
|
5197
5427
|
}
|
|
5198
|
-
var
|
|
5199
|
-
|
|
5200
|
-
|
|
5201
|
-
|
|
5202
|
-
|
|
5203
|
-
|
|
5204
|
-
|
|
5205
|
-
|
|
5206
|
-
|
|
5207
|
-
|
|
5208
|
-
|
|
5209
|
-
|
|
5210
|
-
|
|
5211
|
-
|
|
5212
|
-
|
|
5213
|
-
|
|
5214
|
-
|
|
5428
|
+
var WORKFLOW_HOST_ENTRY_PATHS = ["src/index.ts", "dist/src/index.js", "starter/index.ts", "dist/starter/index.js", "subagents/index.ts", "dist/subagents/index.js"];
|
|
5429
|
+
function isWorkflowHostEntry(path) {
|
|
5430
|
+
const entryPath = canonicalPath(path);
|
|
5431
|
+
let packageRoot2 = dirname5(entryPath);
|
|
5432
|
+
while (packageRoot2 !== dirname5(packageRoot2)) {
|
|
5433
|
+
const packageMetadataPath = join8(packageRoot2, "package.json");
|
|
5434
|
+
if (existsSync(packageMetadataPath)) {
|
|
5435
|
+
try {
|
|
5436
|
+
const metadata = JSON.parse(readFileSync5(packageMetadataPath, "utf8"));
|
|
5437
|
+
if (typeof metadata !== "object" || metadata === null || !("name" in metadata) || !isCorePackageName(metadata.name)) return false;
|
|
5438
|
+
return WORKFLOW_HOST_ENTRY_PATHS.some((entry) => canonicalPath(join8(packageRoot2, entry)) === entryPath);
|
|
5439
|
+
} catch {
|
|
5440
|
+
return false;
|
|
5441
|
+
}
|
|
5442
|
+
}
|
|
5443
|
+
packageRoot2 = dirname5(packageRoot2);
|
|
5444
|
+
}
|
|
5445
|
+
return false;
|
|
5215
5446
|
}
|
|
5216
5447
|
var WORKFLOW_DIRECTORY = "pi-extensible-workflows";
|
|
5217
5448
|
function workflowSystemPromptPath(cwd, agentDir, projectTrusted2) {
|
|
5218
|
-
const projectPath =
|
|
5449
|
+
const projectPath = join8(cwd, ".pi", WORKFLOW_DIRECTORY, "SYSTEM.md");
|
|
5219
5450
|
if (projectTrusted2 && existsSync(projectPath)) return projectPath;
|
|
5220
|
-
const globalPaths = [
|
|
5451
|
+
const globalPaths = [join8(homedir5(), ".pi", WORKFLOW_DIRECTORY, "SYSTEM.md"), join8(agentDir, WORKFLOW_DIRECTORY, "SYSTEM.md")];
|
|
5221
5452
|
return globalPaths.find((path) => existsSync(path));
|
|
5222
5453
|
}
|
|
5223
5454
|
function filterContextFiles(base, scopes, cwd, agentDir) {
|
|
5224
5455
|
const globalPath = canonicalPath(agentDir);
|
|
5225
5456
|
const cwdPath = canonicalPath(cwd);
|
|
5226
5457
|
return base.filter(({ path }) => {
|
|
5227
|
-
const resolvedPath = canonicalPath(
|
|
5458
|
+
const resolvedPath = canonicalPath(dirname5(path));
|
|
5228
5459
|
return scopes.some((scope) => scope === "global" ? resolvedPath === globalPath : scope === "cwd" ? resolvedPath === cwdPath : resolvedPath !== globalPath && resolvedPath !== cwdPath);
|
|
5229
5460
|
});
|
|
5230
5461
|
}
|
|
@@ -5245,33 +5476,40 @@ function loadPiPackageDirectory() {
|
|
|
5245
5476
|
}
|
|
5246
5477
|
}).catch((error) => ({ error: `could not load @earendil-works/pi-coding-agent package metadata: ${errorText(error)}` }));
|
|
5247
5478
|
}
|
|
5248
|
-
var
|
|
5249
|
-
async function
|
|
5479
|
+
var nativeCoreModules = /* @__PURE__ */ new Map();
|
|
5480
|
+
async function loadNativeCoreModule(file, label) {
|
|
5250
5481
|
const packageDirectory = await loadPiPackageDirectory();
|
|
5251
|
-
let
|
|
5482
|
+
let modulePath;
|
|
5252
5483
|
if (packageDirectory.directory) {
|
|
5253
|
-
const candidate =
|
|
5254
|
-
if (existsSync(candidate))
|
|
5484
|
+
const candidate = resolve6(packageDirectory.directory, "dist/core", file);
|
|
5485
|
+
if (existsSync(candidate)) modulePath = candidate;
|
|
5255
5486
|
}
|
|
5256
|
-
|
|
5487
|
+
modulePath ??= resolve6(dirname5(fileURLToPath3(import.meta.resolve("@earendil-works/pi-coding-agent"))), "core", file);
|
|
5257
5488
|
const packageDirectoryError = packageDirectory.error ? ` Package directory lookup failed: ${packageDirectory.error}` : "";
|
|
5258
|
-
|
|
5259
|
-
|
|
5260
|
-
|
|
5489
|
+
let loaded = nativeCoreModules.get(file);
|
|
5490
|
+
if (!loaded) {
|
|
5491
|
+
loaded = import(modulePath).catch((error) => {
|
|
5492
|
+
throw new Error(`Could not load Pi ${label}.${packageDirectoryError}`, { cause: error });
|
|
5493
|
+
});
|
|
5494
|
+
nativeCoreModules.set(file, loaded);
|
|
5495
|
+
}
|
|
5496
|
+
return loaded;
|
|
5261
5497
|
}
|
|
5262
5498
|
async function expandPromptTemplateForInspection(text, templates) {
|
|
5263
|
-
return (await
|
|
5499
|
+
return (await loadNativeCoreModule("prompt-templates.js", "prompt templates")).expandPromptTemplate(text, templates);
|
|
5500
|
+
}
|
|
5501
|
+
async function buildSystemPromptForInspection(options) {
|
|
5502
|
+
return (await loadNativeCoreModule("system-prompt.js", "system prompt builder")).buildSystemPrompt(options);
|
|
5264
5503
|
}
|
|
5265
5504
|
async function preparePiPrompt(native, text) {
|
|
5266
5505
|
const session = native;
|
|
5267
5506
|
const diagnostics = [];
|
|
5268
5507
|
const runner = session._extensionRunner;
|
|
5269
|
-
const baseSystemPrompt =
|
|
5508
|
+
const baseSystemPrompt = session.systemPrompt ?? "";
|
|
5270
5509
|
const baseOptions = session._baseSystemPromptOptions;
|
|
5271
5510
|
const expandSkillCommand = session._expandSkillCommand;
|
|
5272
5511
|
const templates = session.promptTemplates;
|
|
5273
5512
|
if (!runner) diagnostics.push({ type: "error", message: "Pi prompt inspection seam is unavailable: extension runner is missing", source: "Pi session" });
|
|
5274
|
-
if (typeof session._baseSystemPrompt !== "string") diagnostics.push({ type: "error", message: "Pi prompt inspection seam is unavailable: base system prompt is missing", source: "Pi session" });
|
|
5275
5513
|
if (baseOptions === void 0) diagnostics.push({ type: "error", message: "Pi prompt inspection seam is unavailable: base system prompt options are missing", source: "Pi session" });
|
|
5276
5514
|
if (typeof expandSkillCommand !== "function") diagnostics.push({ type: "error", message: "Pi prompt inspection seam is unavailable: skill expansion is missing", source: "Pi session" });
|
|
5277
5515
|
if (!Array.isArray(templates)) diagnostics.push({ type: "error", message: "Pi prompt inspection seam is unavailable: prompt templates are missing", source: "Pi session" });
|
|
@@ -5296,9 +5534,11 @@ async function preparePiPrompt(native, text) {
|
|
|
5296
5534
|
diagnostics.push({ type: "error", message: `Pi prompt template expansion is unavailable: ${errorText(error)}`, source: "Pi session" });
|
|
5297
5535
|
}
|
|
5298
5536
|
}
|
|
5299
|
-
const result = !inputHandled && runner && baseOptions !== void 0 ? await runner.emitBeforeAgentStart(expandedPrompt, void 0,
|
|
5537
|
+
const result = !inputHandled && runner && baseOptions !== void 0 ? await runner.emitBeforeAgentStart(expandedPrompt, void 0, baseOptions) : void 0;
|
|
5300
5538
|
const prepared = result;
|
|
5301
|
-
|
|
5539
|
+
const systemPromptOptions = prepared?.systemPromptOptions ?? baseOptions;
|
|
5540
|
+
const systemPrompt = prepared?.systemPromptOptions === void 0 ? baseSystemPrompt : await buildSystemPromptForInspection(prepared.systemPromptOptions);
|
|
5541
|
+
return { prompt: text, expandedPrompt, systemPrompt, systemPromptOptions, messages: prepared?.messages ?? [], inputHandled, diagnostics };
|
|
5302
5542
|
} finally {
|
|
5303
5543
|
unsubscribe?.();
|
|
5304
5544
|
}
|
|
@@ -5327,40 +5567,56 @@ function flushExtensionProviders(resourceLoader, modelRuntime) {
|
|
|
5327
5567
|
runtime.pendingNativeProviderRegistrations = [];
|
|
5328
5568
|
return failures;
|
|
5329
5569
|
}
|
|
5570
|
+
function builtinExtensions() {
|
|
5571
|
+
return [
|
|
5572
|
+
{ name: "codemode", factory: createCodemodeExtension(), replaceable: true, builtin: true },
|
|
5573
|
+
{ name: "tool-search", factory: createToolSearchExtension(), replaceable: true, builtin: true },
|
|
5574
|
+
{ name: "mcp", factory: createMcpExtension(), replaceable: true, builtin: true }
|
|
5575
|
+
];
|
|
5576
|
+
}
|
|
5577
|
+
var BUILTIN_EXTENSION_PREFIX = "builtin:";
|
|
5578
|
+
function isLlamaModel(model) {
|
|
5579
|
+
return model.provider === "llama.cpp";
|
|
5580
|
+
}
|
|
5330
5581
|
async function createLocalPiSessionHandle(input, sessionStartEvent) {
|
|
5331
|
-
const agentDir = input.agentDir ??
|
|
5582
|
+
const agentDir = input.agentDir ?? getAgentDir3();
|
|
5332
5583
|
const systemPromptSource = workflowSystemPromptPath(input.cwd, agentDir, input.resourcePolicy?.projectTrusted ?? true);
|
|
5333
5584
|
const systemPromptOptions = input.systemPrompt !== void 0 ? { systemPromptOverride: () => input.systemPrompt } : systemPromptSource !== void 0 ? { systemPrompt: systemPromptSource } : {};
|
|
5334
5585
|
const contextFilesOverride = input.contextFiles === void 0 ? void 0 : (base) => ({ ...base, agentsFiles: filterContextFiles(base.agentsFiles, input.contextFiles ?? [], input.cwd, agentDir) });
|
|
5335
|
-
const manager = input.sessionManager ?? (input.sessionPath ? SessionManager.open(input.sessionPath,
|
|
5586
|
+
const manager = input.sessionManager ?? (input.sessionPath ? SessionManager.open(input.sessionPath, join8(agentDir, "sessions"), input.cwd) : input.agentDir ? SessionManager.create(input.cwd, join8(agentDir, "sessions")) : SessionManager.create(input.cwd));
|
|
5336
5587
|
if (!input.sessionPath) manager.appendSessionInfo(input.sessionLabel);
|
|
5337
|
-
const modelRuntime = await ModelRuntime.create({ authPath:
|
|
5588
|
+
const modelRuntime = await ModelRuntime.create({ authPath: join8(agentDir, "auth.json"), modelsPath: join8(agentDir, "models.json") });
|
|
5338
5589
|
const customTools = [...input.customTools ?? [], ...input.resultTool ? [input.resultTool] : []];
|
|
5339
5590
|
const tools = [.../* @__PURE__ */ new Set([...input.tools, ...customTools.map(({ name }) => name)])];
|
|
5340
5591
|
let settingsManager;
|
|
5341
5592
|
let resourceLoader;
|
|
5593
|
+
const builtins = builtinExtensions();
|
|
5594
|
+
const builtinNames = builtins.flatMap((extension) => typeof extension === "function" ? [] : [extension.name]);
|
|
5595
|
+
const extensionFactories = [...builtins, ...input.extensionFactories ?? []];
|
|
5342
5596
|
const policy = input.resourcePolicy;
|
|
5343
5597
|
if (policy) {
|
|
5344
5598
|
settingsManager = SettingsManager.create(input.cwd, agentDir, { projectTrusted: false });
|
|
5345
5599
|
settingsManager.setProjectTrusted(policy.projectTrusted);
|
|
5346
|
-
const packageManager = new DefaultPackageManager({ cwd: input.cwd, agentDir, settingsManager });
|
|
5600
|
+
const packageManager = new DefaultPackageManager({ cwd: input.cwd, agentDir, settingsManager, builtinExtensions: builtinNames });
|
|
5347
5601
|
const resolved = await packageManager.resolve();
|
|
5348
|
-
const discoveredExtensions = [...new Set(resolved.extensions.filter(({ enabled, metadata }) => enabled && (policy.projectTrusted || metadata.scope !== "project")).map(({ path }) =>
|
|
5602
|
+
const discoveredExtensions = [...new Set(resolved.extensions.filter(({ enabled, metadata }) => enabled && (policy.projectTrusted || metadata.scope !== "project")).map(({ path }) => extensionIdentity(path)))];
|
|
5349
5603
|
const selectorSources = policy.selectorSources;
|
|
5350
|
-
const
|
|
5351
|
-
|
|
5352
|
-
|
|
5353
|
-
|
|
5354
|
-
|
|
5604
|
+
const selection = resolveRole(void 0, {
|
|
5605
|
+
cwd: input.cwd,
|
|
5606
|
+
selectorSources,
|
|
5607
|
+
resources: { extensions: discoveredExtensions }
|
|
5608
|
+
});
|
|
5609
|
+
const selectedExtensions = selection.selectedExtensions ?? [];
|
|
5610
|
+
const extensionPaths = selectedExtensions.filter((path) => path.startsWith(BUILTIN_EXTENSION_PREFIX) || !isWorkflowHostEntry(path));
|
|
5355
5611
|
policy.selectedExtensions = selectedExtensions;
|
|
5356
|
-
policy.unmatchedExtensions = unmatchedExtensions;
|
|
5612
|
+
policy.unmatchedExtensions = selection.unmatchedExtensions ?? [];
|
|
5357
5613
|
const skillPaths = [...new Set(resolved.skills.filter(({ enabled, metadata }) => enabled && (policy.projectTrusted || metadata.scope !== "project")).map(({ path }) => path))];
|
|
5358
5614
|
const updateSkillMatches = (skills) => {
|
|
5359
5615
|
const names = [...new Set(skills.map(({ name }) => name))];
|
|
5360
|
-
const
|
|
5361
|
-
const selectedSkills =
|
|
5616
|
+
const selected = resolveRole(void 0, { cwd: input.cwd, selectorSources, resources: { skills: names } });
|
|
5617
|
+
const selectedSkills = selected.selectedSkills ?? [];
|
|
5362
5618
|
policy.selectedSkills = selectedSkills;
|
|
5363
|
-
policy.unmatchedSkills =
|
|
5619
|
+
policy.unmatchedSkills = selected.unmatchedSkills ?? [];
|
|
5364
5620
|
return new Set(names.filter((name) => !selectedSkills.includes(name)));
|
|
5365
5621
|
};
|
|
5366
5622
|
resourceLoader = new DefaultResourceLoader({
|
|
@@ -5371,7 +5627,7 @@ async function createLocalPiSessionHandle(input, sessionStartEvent) {
|
|
|
5371
5627
|
additionalExtensionPaths: extensionPaths,
|
|
5372
5628
|
noSkills: true,
|
|
5373
5629
|
additionalSkillPaths: [.../* @__PURE__ */ new Set([...skillPaths, ...input.additionalSkillPaths ?? []])],
|
|
5374
|
-
|
|
5630
|
+
extensionFactories,
|
|
5375
5631
|
...contextFilesOverride ? { agentsFilesOverride: contextFilesOverride } : {},
|
|
5376
5632
|
skillsOverride: (base) => {
|
|
5377
5633
|
const disabledSkills = updateSkillMatches(base.skills);
|
|
@@ -5383,17 +5639,17 @@ async function createLocalPiSessionHandle(input, sessionStartEvent) {
|
|
|
5383
5639
|
await resourceLoader.reload();
|
|
5384
5640
|
} else {
|
|
5385
5641
|
settingsManager = SettingsManager.create(input.cwd, agentDir, { projectTrusted: true });
|
|
5386
|
-
const packageManager = new DefaultPackageManager({ cwd: input.cwd, agentDir, settingsManager });
|
|
5642
|
+
const packageManager = new DefaultPackageManager({ cwd: input.cwd, agentDir, settingsManager, builtinExtensions: builtinNames });
|
|
5387
5643
|
const resolved = await packageManager.resolve();
|
|
5388
|
-
const extensionPaths = [...new Set(resolved.extensions.filter(({ enabled }) => enabled).map(({ path }) =>
|
|
5389
|
-
resourceLoader = new DefaultResourceLoader({ cwd: input.cwd, agentDir, settingsManager, noExtensions: true, additionalExtensionPaths: extensionPaths, ...input.additionalSkillPaths?.length ? { additionalSkillPaths: [...input.additionalSkillPaths] } : {},
|
|
5644
|
+
const extensionPaths = [...new Set(resolved.extensions.filter(({ enabled }) => enabled).map(({ path }) => extensionIdentity(path)).filter((path) => path.startsWith(BUILTIN_EXTENSION_PREFIX) || !isWorkflowHostEntry(path)))];
|
|
5645
|
+
resourceLoader = new DefaultResourceLoader({ cwd: input.cwd, agentDir, settingsManager, noExtensions: true, additionalExtensionPaths: extensionPaths, ...input.additionalSkillPaths?.length ? { additionalSkillPaths: [...input.additionalSkillPaths] } : {}, extensionFactories, ...contextFilesOverride ? { agentsFilesOverride: contextFilesOverride } : {}, ...systemPromptOptions, ...input.systemPromptAppend ? { appendSystemPromptOverride: (base) => [...base, input.systemPromptAppend ?? ""] } : {} });
|
|
5390
5646
|
await resourceLoader.reload();
|
|
5391
5647
|
}
|
|
5392
5648
|
const providerFailures = flushExtensionProviders(resourceLoader, modelRuntime);
|
|
5393
5649
|
await modelRuntime.refresh({ allowNetwork: false });
|
|
5394
5650
|
const model = modelRuntime.getModel(input.model.provider, input.model.model);
|
|
5395
5651
|
if (!model) {
|
|
5396
|
-
const because = providerFailures.length > 0 ? ` (provider registration failed \u2014 ${providerFailures.join("; ")})` : "";
|
|
5652
|
+
const because = providerFailures.length > 0 ? ` (provider registration failed \u2014 ${providerFailures.join("; ")})` : isLlamaModel(input.model) ? " (Pi does not expose its built-in llama.cpp provider to extension sessions; declare the llama.cpp server as a provider in models.json and use that model instead)" : "";
|
|
5397
5653
|
throw new WorkflowError("UNKNOWN_MODEL", `Unknown model: ${input.model.provider}/${input.model.model}${because}`);
|
|
5398
5654
|
}
|
|
5399
5655
|
const effectiveSessionStartEvent = input.settings === void 0 && sessionStartEvent === void 0 ? void 0 : { ...sessionStartEvent ?? { type: "session_start", reason: "startup" }, settings: input.settings ?? Object.freeze({}) };
|
|
@@ -5418,13 +5674,13 @@ async function createLocalPiSessionHandle(input, sessionStartEvent) {
|
|
|
5418
5674
|
await shutdown("quit").catch(() => void 0);
|
|
5419
5675
|
throw error;
|
|
5420
5676
|
}
|
|
5421
|
-
const resourcePaths = { extensions: resourceLoader.getExtensions().extensions.filter(({ path }) => !path.startsWith("<")).map(({ resolvedPath }) =>
|
|
5677
|
+
const resourcePaths = { extensions: resourceLoader.getExtensions().extensions.filter(({ path }) => !path.startsWith("<")).map(({ resolvedPath }) => extensionIdentity(resolvedPath)), skills: resourceLoader.getSkills().skills.map(({ filePath }) => canonicalPath(filePath)) };
|
|
5422
5678
|
const resourceInspection = () => {
|
|
5423
5679
|
const extensions = resourceLoader.getExtensions();
|
|
5424
5680
|
const skills = resourceLoader.getSkills();
|
|
5425
5681
|
const diagnostics = [...extensions.errors.map(({ path, error }) => ({ type: "error", message: error, source: path })), ...skills.diagnostics.map(({ type, path, message }) => ({ type, message, ...path ? { source: path } : {} }))];
|
|
5426
5682
|
const systemSource = systemPromptSource;
|
|
5427
|
-
return { extensions: resourceLoader.getExtensions().extensions.filter(({ path }) => !path.startsWith("<")).map(({ resolvedPath }) =>
|
|
5683
|
+
return { extensions: resourceLoader.getExtensions().extensions.filter(({ path }) => !path.startsWith("<")).map(({ resolvedPath }) => extensionIdentity(resolvedPath)), skills: skills.skills.map(({ name }) => name), diagnostics, ...systemSource ? { systemPromptSource: systemSource } : resourceLoader.getSystemPrompt() !== void 0 ? { systemPromptSource: "Pi resource loader" } : {} };
|
|
5428
5684
|
};
|
|
5429
5685
|
const managedSession = Object.assign(session, {
|
|
5430
5686
|
getLeafId: () => manager.getLeafId(),
|
|
@@ -5852,27 +6108,27 @@ function appendResourcePolicyNarrowing(ceiling, candidate) {
|
|
|
5852
6108
|
};
|
|
5853
6109
|
}
|
|
5854
6110
|
function packageRoot(start) {
|
|
5855
|
-
let current =
|
|
6111
|
+
let current = dirname5(realpathSync3(start));
|
|
5856
6112
|
for (; ; ) {
|
|
5857
|
-
const candidates = [current,
|
|
6113
|
+
const candidates = [current, join8(current, "..", "@earendil-works", "pi-coding-agent"), join8(current, "..", "pi-coding-agent")];
|
|
5858
6114
|
for (const candidate of candidates) {
|
|
5859
|
-
const packageJsonPath =
|
|
6115
|
+
const packageJsonPath = join8(candidate, "package.json");
|
|
5860
6116
|
if (!existsSync(packageJsonPath)) continue;
|
|
5861
|
-
const packageJson = JSON.parse(
|
|
6117
|
+
const packageJson = JSON.parse(readFileSync5(packageJsonPath, "utf8"));
|
|
5862
6118
|
if (object(packageJson) && typeof packageJson.name === "string" && packageJson.name === "@earendil-works/pi-coding-agent") return candidate;
|
|
5863
6119
|
}
|
|
5864
|
-
const parent =
|
|
6120
|
+
const parent = dirname5(current);
|
|
5865
6121
|
if (parent === current) return void 0;
|
|
5866
6122
|
current = parent;
|
|
5867
6123
|
}
|
|
5868
6124
|
}
|
|
5869
6125
|
function piPackageEntrypoint(root) {
|
|
5870
|
-
const packageJson = JSON.parse(
|
|
6126
|
+
const packageJson = JSON.parse(readFileSync5(join8(root, "package.json"), "utf8"));
|
|
5871
6127
|
if (!object(packageJson)) throw new Error("the @earendil-works/pi-coding-agent package does not declare a pi CLI");
|
|
5872
6128
|
const bin = packageJson.bin;
|
|
5873
6129
|
const cli = typeof bin === "string" ? bin : object(bin) && typeof bin.pi === "string" ? bin.pi : void 0;
|
|
5874
6130
|
if (!cli) throw new Error("the @earendil-works/pi-coding-agent package does not declare a pi CLI");
|
|
5875
|
-
const entrypoint =
|
|
6131
|
+
const entrypoint = resolve6(root, cli);
|
|
5876
6132
|
if (!existsSync(entrypoint)) throw new Error(`the originating Pi CLI entrypoint is unavailable: ${entrypoint}`);
|
|
5877
6133
|
return entrypoint;
|
|
5878
6134
|
}
|
|
@@ -5913,7 +6169,7 @@ ${instruction}` : ""}`;
|
|
|
5913
6169
|
}
|
|
5914
6170
|
async function preparedAgentSession(input, initialPrompt) {
|
|
5915
6171
|
const { runtime: piRuntime, error: piRuntimeError } = await piRuntimeResolution;
|
|
5916
|
-
const systemPromptPath = input.systemPrompt === void 0 ? workflowSystemPromptPath(input.cwd, input.agentDir ??
|
|
6172
|
+
const systemPromptPath = input.systemPrompt === void 0 ? workflowSystemPromptPath(input.cwd, input.agentDir ?? getAgentDir3(), input.resourcePolicy?.projectTrusted ?? true) : void 0;
|
|
5917
6173
|
const prepared = {
|
|
5918
6174
|
cwd: input.cwd,
|
|
5919
6175
|
model: Object.freeze({ ...input.model }),
|
|
@@ -5943,7 +6199,7 @@ function agentSetupSummary(setup, hookNames) {
|
|
|
5943
6199
|
return { hookNames: [...hookNames], model: { provider: model.provider, model: model.model, ...model.thinking ? { thinking: model.thinking } : {} }, tools: [...setup.sessionInput.tools, ...setup.sessionInput.resultTool ? [setup.sessionInput.resultTool.name] : []], cwd: setup.sessionInput.cwd, ...setup.sessionInput.resourcePolicy ? { resourceSelectors: resourcePolicySummary(setup.sessionInput.resourcePolicy, setup.sessionInput.tools) } : {} };
|
|
5944
6200
|
}
|
|
5945
6201
|
async function attemptSessionInput(source, attempt) {
|
|
5946
|
-
const target =
|
|
6202
|
+
const target = join8(dirname5(source), `${basename4(source, ".jsonl")}-attempt-${String(attempt)}.jsonl`);
|
|
5947
6203
|
await copyFile(source, target);
|
|
5948
6204
|
return target;
|
|
5949
6205
|
}
|
|
@@ -6053,7 +6309,6 @@ var WorkflowAgentExecutor = class {
|
|
|
6053
6309
|
const roleName = typeof options.role === "string" ? options.role : void 0;
|
|
6054
6310
|
const roleDefinition = roleName ? this.root.agentDefinitions?.[roleName] : void 0;
|
|
6055
6311
|
if (roleName && !roleDefinition) throw new WorkflowError("UNKNOWN_AGENT_TYPE", `Unknown agent role: ${roleName}`);
|
|
6056
|
-
const candidateTools = inheritedTools !== void 0 ? [...inheritedTools] : [...this.root.tools];
|
|
6057
6312
|
if (inheritedTools === void 0) for (const pattern of roleDefinition?.tools ?? []) {
|
|
6058
6313
|
const body = pattern.startsWith("!") ? pattern.slice(1) : pattern;
|
|
6059
6314
|
if (!pattern.startsWith("!") && !resourcePatternHasMagic(pattern) && !this.root.tools.has(body)) {
|
|
@@ -6064,33 +6319,30 @@ var WorkflowAgentExecutor = class {
|
|
|
6064
6319
|
}
|
|
6065
6320
|
}
|
|
6066
6321
|
}
|
|
6067
|
-
const
|
|
6068
|
-
this.root.
|
|
6069
|
-
roleDefinition
|
|
6070
|
-
|
|
6071
|
-
|
|
6072
|
-
|
|
6073
|
-
|
|
6074
|
-
|
|
6075
|
-
|
|
6076
|
-
|
|
6077
|
-
|
|
6078
|
-
|
|
6079
|
-
|
|
6080
|
-
|
|
6081
|
-
|
|
6082
|
-
|
|
6083
|
-
|
|
6084
|
-
|
|
6085
|
-
|
|
6086
|
-
|
|
6087
|
-
|
|
6088
|
-
|
|
6089
|
-
|
|
6090
|
-
|
|
6091
|
-
const overrideSystemPrompt = roleDefinition?.overrideSystemPrompt === true;
|
|
6092
|
-
const contextFiles = options.contextFiles ?? roleDefinition?.contextFiles;
|
|
6093
|
-
return { model, ...alias && requestedModel ? { requestedModel } : {}, tools: requested, ...overrideSystemPrompt ? { systemPrompt: roleDefinition.prompt ?? "" } : {}, systemPromptAppend: overrideSystemPrompt ? "" : roleDefinition?.prompt ?? "", ...contextFiles === void 0 ? {} : { contextFiles: [...contextFiles] } };
|
|
6322
|
+
const resolved = resolveRole(roleName, {
|
|
6323
|
+
cwd: this.root.cwd,
|
|
6324
|
+
...roleDefinition === void 0 ? {} : { definition: roleDefinition },
|
|
6325
|
+
...this.root.agentDefinitions === void 0 ? {} : { definitions: this.root.agentDefinitions },
|
|
6326
|
+
...this.root.agentDir === void 0 ? {} : { agentDir: this.root.agentDir },
|
|
6327
|
+
selectorSources: { global: this.root.resourceSelectors ?? {}, project: {} },
|
|
6328
|
+
...this.root.modelAliases === void 0 ? {} : { modelAliases: this.root.modelAliases },
|
|
6329
|
+
...this.root.knownModels === void 0 ? {} : { knownModels: this.root.knownModels },
|
|
6330
|
+
...this.root.availableModels === void 0 ? {} : { availableModels: this.root.availableModels },
|
|
6331
|
+
...this.root.blockedAliases === void 0 ? {} : { blockedAliases: this.root.blockedAliases },
|
|
6332
|
+
...this.root.blockedAliasTargets === void 0 ? {} : { blockedAliasTargets: this.root.blockedAliasTargets },
|
|
6333
|
+
...this.root.settingsPath === void 0 ? {} : { settingsPath: this.root.settingsPath },
|
|
6334
|
+
rootModel: this.root.model,
|
|
6335
|
+
rootTools: this.root.tools,
|
|
6336
|
+
...inheritedTools === void 0 ? {} : { inheritedTools },
|
|
6337
|
+
...options.model === void 0 ? {} : { model: options.model },
|
|
6338
|
+
...options.modelOverride === void 0 ? {} : { modelOverride: options.modelOverride },
|
|
6339
|
+
...options.tools === void 0 ? {} : { tools: options.tools },
|
|
6340
|
+
...options.skills === void 0 ? {} : { skills: options.skills },
|
|
6341
|
+
...options.extensions === void 0 ? {} : { extensions: options.extensions },
|
|
6342
|
+
...options.contextFiles === void 0 ? {} : { contextFiles: options.contextFiles },
|
|
6343
|
+
...options.effectiveTools === void 0 ? {} : { effectiveTools: options.effectiveTools }
|
|
6344
|
+
});
|
|
6345
|
+
return { model: physicalModel(resolved.model ?? this.root.model, this.root.modelAliases, this.root.knownModels, this.root.settingsPath), ...resolved.requestedModel === void 0 ? {} : { requestedModel: resolved.requestedModel }, tools: resolved.tools ?? [], ...resolved.overrideSystemPrompt ? { systemPrompt: resolved.prompt } : {}, systemPromptAppend: resolved.overrideSystemPrompt ? "" : resolved.prompt, ...resolved.contextFiles === void 0 ? {} : { contextFiles: resolved.contextFiles } };
|
|
6094
6346
|
}
|
|
6095
6347
|
async execute(task, options, signal, customTools = [], setSteer, beforeRetry) {
|
|
6096
6348
|
const executionSignal = signal ?? this.root.runContext?.signal;
|
|
@@ -6376,12 +6628,12 @@ var FairAgentScheduler = class {
|
|
|
6376
6628
|
const id = `${runId}:${String(++this.#nextId)}`;
|
|
6377
6629
|
const tuiIndex = ++run.nextIndex;
|
|
6378
6630
|
let resolveResult = () => void 0;
|
|
6379
|
-
const promise = new Promise((
|
|
6380
|
-
resolveResult =
|
|
6631
|
+
const promise = new Promise((resolve9) => {
|
|
6632
|
+
resolveResult = resolve9;
|
|
6381
6633
|
});
|
|
6382
6634
|
let resolveCompletion = () => void 0;
|
|
6383
|
-
const completion = new Promise((
|
|
6384
|
-
resolveCompletion =
|
|
6635
|
+
const completion = new Promise((resolve9) => {
|
|
6636
|
+
resolveCompletion = resolve9;
|
|
6385
6637
|
});
|
|
6386
6638
|
const node = { id, runId, ...parentId ? { parentId } : {}, prompt, options: effective, children: /* @__PURE__ */ new Set(), collected: false, collecting: false, resultQueue: Promise.resolve(), state: "queued", controller: new AbortController(), promise, resolve: resolveResult, completion, resolveCompletion, task: async () => void 0, restored: false };
|
|
6387
6639
|
node.task = async () => {
|
|
@@ -6419,8 +6671,8 @@ var FairAgentScheduler = class {
|
|
|
6419
6671
|
const childPromise = child.promise;
|
|
6420
6672
|
const previousCollection = parent.resultQueue;
|
|
6421
6673
|
let releaseCollection = () => void 0;
|
|
6422
|
-
parent.resultQueue = new Promise((
|
|
6423
|
-
releaseCollection =
|
|
6674
|
+
parent.resultQueue = new Promise((resolve9) => {
|
|
6675
|
+
releaseCollection = resolve9;
|
|
6424
6676
|
});
|
|
6425
6677
|
try {
|
|
6426
6678
|
await previousCollection;
|
|
@@ -6429,9 +6681,9 @@ var FairAgentScheduler = class {
|
|
|
6429
6681
|
this.#persist(parent.runId);
|
|
6430
6682
|
this.#release(parent.runId);
|
|
6431
6683
|
const outcome = await childPromise;
|
|
6432
|
-
await new Promise((
|
|
6684
|
+
await new Promise((resolve9) => {
|
|
6433
6685
|
this.#enqueue(parent.runId, void 0, () => {
|
|
6434
|
-
|
|
6686
|
+
resolve9();
|
|
6435
6687
|
});
|
|
6436
6688
|
});
|
|
6437
6689
|
if (this.#cancelledOrSettled(parent)) {
|
|
@@ -6519,6 +6771,7 @@ var FairAgentScheduler = class {
|
|
|
6519
6771
|
label: "Child Agent",
|
|
6520
6772
|
description: "Start a direct child agent",
|
|
6521
6773
|
parameters: Type.Object({ prompt: Type.String(), label: Type.String(), tools: Type.Optional(Type.Array(Type.String())), skills: Type.Optional(Type.Array(Type.String())), extensions: Type.Optional(Type.Array(Type.String())), model: Type.Optional(Type.String()), role: Type.Optional(Type.String()), contextFiles: Type.Optional(Type.Array(Type.String())), outputSchema: Type.Optional(Type.Record(Type.String(), Type.Unknown())), retries: Type.Optional(Type.Integer({ minimum: 0 })), timeoutMs: Type.Optional(Type.Union([Type.Integer({ minimum: 1 }), Type.Null()])) }, { additionalProperties: true }),
|
|
6774
|
+
outputSchema: Type.Object({ id: Type.String() }),
|
|
6522
6775
|
execute: async (_id, params) => {
|
|
6523
6776
|
if (!isChildAgentToolParams(params)) throw new WorkflowError("INVALID_METADATA", "Invalid child agent parameters");
|
|
6524
6777
|
validateAgentOptions(params);
|
|
@@ -6529,7 +6782,7 @@ var FairAgentScheduler = class {
|
|
|
6529
6782
|
Reflect.deleteProperty(agentOptions, "prompt");
|
|
6530
6783
|
const options = { label: params.label, requestedLabel: params.label, cwd: parent.options.cwd, tools, agentOptions, ...params.skills ? { skills: params.skills } : {}, ...params.extensions ? { extensions: params.extensions } : {}, ...params.model ? { model: params.model } : {}, ...params.role ? { role: params.role } : {}, ...params.contextFiles ? { contextFiles: params.contextFiles } : {}, ...outputSchema === void 0 ? {} : { schema: outputSchema }, ...params.retries === void 0 ? {} : { retries: params.retries }, ...params.timeoutMs === void 0 ? {} : { timeoutMs: params.timeoutMs } };
|
|
6531
6784
|
const child = this.spawn(parent.runId, params.prompt, options, parentId);
|
|
6532
|
-
return { content: [{ type: "text", text: JSON.stringify({ id: child.id }) }], details: { id: child.id } };
|
|
6785
|
+
return { content: [{ type: "text", text: JSON.stringify({ id: child.id }) }], details: { id: child.id }, structuredContent: { id: child.id } };
|
|
6533
6786
|
}
|
|
6534
6787
|
});
|
|
6535
6788
|
const resultTool = defineTool({
|
|
@@ -6537,10 +6790,11 @@ var FairAgentScheduler = class {
|
|
|
6537
6790
|
label: "Child Result",
|
|
6538
6791
|
description: "Wait for a direct child and return its result once; repeated retrieval fails with AGENT_RESULT_COLLECTED",
|
|
6539
6792
|
parameters: Type.Object({ id: Type.String() }),
|
|
6793
|
+
outputSchema: Type.Object({ id: Type.String(), ok: Type.Boolean(), value: Type.Optional(Type.Unknown()), error: Type.Optional(Type.Object({ code: Type.String(), message: Type.String() })) }),
|
|
6540
6794
|
execute: async (_id, params) => {
|
|
6541
6795
|
const value = await this.result(parentId, params.id);
|
|
6542
6796
|
if (!value.ok && value.error.code === "BUDGET_EXHAUSTED") throw new WorkflowError("BUDGET_EXHAUSTED", value.error.message);
|
|
6543
|
-
return { content: [{ type: "text", text: JSON.stringify(value) }], details: value };
|
|
6797
|
+
return { content: [{ type: "text", text: JSON.stringify(value) }], details: value, structuredContent: value };
|
|
6544
6798
|
}
|
|
6545
6799
|
});
|
|
6546
6800
|
const steerTool = defineTool({
|
|
@@ -6564,12 +6818,12 @@ var FairAgentScheduler = class {
|
|
|
6564
6818
|
for (const record3 of ownership) {
|
|
6565
6819
|
if (record3.id.split(":").slice(0, -1).join(":") !== runId) throw new WorkflowError("RESUME_INCOMPATIBLE", `Persisted agent belongs to another run: ${record3.id}`);
|
|
6566
6820
|
let resolveResult = () => void 0;
|
|
6567
|
-
const promise = new Promise((
|
|
6568
|
-
resolveResult =
|
|
6821
|
+
const promise = new Promise((resolve9) => {
|
|
6822
|
+
resolveResult = resolve9;
|
|
6569
6823
|
});
|
|
6570
6824
|
let resolveCompletion = () => void 0;
|
|
6571
|
-
const completion = new Promise((
|
|
6572
|
-
resolveCompletion =
|
|
6825
|
+
const completion = new Promise((resolve9) => {
|
|
6826
|
+
resolveCompletion = resolve9;
|
|
6573
6827
|
});
|
|
6574
6828
|
const node = { id: record3.id, runId, ...record3.parentId ? { parentId: record3.parentId } : {}, ...record3.prompt === void 0 ? {} : { prompt: record3.prompt }, options: this.#inherit(run, void 0, record3.options), children: /* @__PURE__ */ new Set(), collected: false, collecting: false, resultQueue: Promise.resolve(), state: record3.state, controller: new AbortController(), promise, resolve: resolveResult, completion, resolveCompletion, task: async () => void 0, restored: true };
|
|
6575
6829
|
this.#nodes.set(node.id, node);
|
|
@@ -6658,9 +6912,9 @@ var FairAgentScheduler = class {
|
|
|
6658
6912
|
const child = this.#nodes.get(childId);
|
|
6659
6913
|
if (child && !child.collected) this.#cancelTree(child);
|
|
6660
6914
|
}
|
|
6661
|
-
const
|
|
6915
|
+
const resolve9 = node.resolve;
|
|
6662
6916
|
node.resolve = void 0;
|
|
6663
|
-
|
|
6917
|
+
resolve9?.(result);
|
|
6664
6918
|
node.resolveCompletion();
|
|
6665
6919
|
}
|
|
6666
6920
|
#cancelTree(node) {
|
|
@@ -6723,8 +6977,8 @@ var RunLifecycle = class {
|
|
|
6723
6977
|
return this.#state;
|
|
6724
6978
|
}
|
|
6725
6979
|
async enter() {
|
|
6726
|
-
while (this.#state === "pausing" || this.#state === "paused" || this.#state === "awaiting_input") await new Promise((
|
|
6727
|
-
this.#waiters.push(
|
|
6980
|
+
while (this.#state === "pausing" || this.#state === "paused" || this.#state === "awaiting_input") await new Promise((resolve9) => {
|
|
6981
|
+
this.#waiters.push(resolve9);
|
|
6728
6982
|
});
|
|
6729
6983
|
if (this.#state !== "running") throw new WorkflowError("CANCELLED", `Run is ${this.#state}`);
|
|
6730
6984
|
this.#active += 1;
|
|
@@ -6734,8 +6988,8 @@ var RunLifecycle = class {
|
|
|
6734
6988
|
if (this.#state === "pausing" && this.#active === 0) await this.#set("paused", "pause");
|
|
6735
6989
|
}
|
|
6736
6990
|
async enterAwaitingInput() {
|
|
6737
|
-
while (this.#state === "pausing" || this.#state === "paused") await new Promise((
|
|
6738
|
-
this.#waiters.push(
|
|
6991
|
+
while (this.#state === "pausing" || this.#state === "paused") await new Promise((resolve9) => {
|
|
6992
|
+
this.#waiters.push(resolve9);
|
|
6739
6993
|
});
|
|
6740
6994
|
if (this.#state === "awaiting_input") return;
|
|
6741
6995
|
if (this.#state !== "running") throw new WorkflowError("RESUME_INCOMPATIBLE", `Cannot await input for ${this.#state} run`);
|
|
@@ -6744,7 +6998,7 @@ var RunLifecycle = class {
|
|
|
6744
6998
|
async resolveAwaitingInput() {
|
|
6745
6999
|
if (this.#state !== "awaiting_input") return;
|
|
6746
7000
|
await this.#set("running", "checkpoint_resolved");
|
|
6747
|
-
for (const
|
|
7001
|
+
for (const resolve9 of this.#waiters.splice(0)) resolve9();
|
|
6748
7002
|
}
|
|
6749
7003
|
async pause() {
|
|
6750
7004
|
if (this.#state !== "running") throw new WorkflowError("RESUME_INCOMPATIBLE", `Cannot pause ${this.#state} run`);
|
|
@@ -6755,7 +7009,7 @@ var RunLifecycle = class {
|
|
|
6755
7009
|
async resume() {
|
|
6756
7010
|
if (this.#state !== "pausing" && this.#state !== "paused" && this.#state !== "interrupted" && this.#state !== "budget_exhausted") throw new WorkflowError("RESUME_INCOMPATIBLE", `Cannot resume ${this.#state} run`);
|
|
6757
7011
|
await this.#set("running", "resume");
|
|
6758
|
-
for (const
|
|
7012
|
+
for (const resolve9 of this.#waiters.splice(0)) resolve9();
|
|
6759
7013
|
}
|
|
6760
7014
|
async providerPause() {
|
|
6761
7015
|
await this.leave();
|
|
@@ -6765,7 +7019,7 @@ var RunLifecycle = class {
|
|
|
6765
7019
|
async terminal(state, reason) {
|
|
6766
7020
|
if (HARD_TERMINAL_RUN_STATES.has(this.#state)) throw new WorkflowError("RESUME_INCOMPATIBLE", `${this.#state} runs are terminal`);
|
|
6767
7021
|
await this.#set(state, reason ?? state);
|
|
6768
|
-
for (const
|
|
7022
|
+
for (const resolve9 of this.#waiters.splice(0)) resolve9();
|
|
6769
7023
|
}
|
|
6770
7024
|
async #set(state, reason) {
|
|
6771
7025
|
const previousState = this.#state;
|
|
@@ -7041,7 +7295,7 @@ function withWorkflowFunctions(bridge, store, runContext, registry, settings = O
|
|
|
7041
7295
|
import { randomUUID as randomUUID4 } from "node:crypto";
|
|
7042
7296
|
|
|
7043
7297
|
// src/host-delivery.ts
|
|
7044
|
-
import { join as
|
|
7298
|
+
import { join as join9 } from "node:path";
|
|
7045
7299
|
import { DEFAULT_MAX_BYTES } from "@earendil-works/pi-coding-agent";
|
|
7046
7300
|
var FAILURE_DELIVERY_STATES = /* @__PURE__ */ new Set(["failed", "stopped", "interrupted", "budget_exhausted"]);
|
|
7047
7301
|
var ForegroundDeliveryController = class {
|
|
@@ -7143,6 +7397,7 @@ var WORKFLOW_ERROR_PROSE = {
|
|
|
7143
7397
|
RUN_NOT_FOUND: (detail) => /^Unknown workflow run\b/.test(detail) ? "The workflow run was not found." : `The workflow run was not found: ${detail}.`,
|
|
7144
7398
|
RPC_LIMIT_EXCEEDED: (detail) => `The workflow communication data exceeded its size limit: ${detail}.`,
|
|
7145
7399
|
SHELL_FAILED: (detail) => `The workflow shell command failed: ${detail}.`,
|
|
7400
|
+
TOOL_FAILED: (detail) => `The workflow tool call failed: ${detail}.`,
|
|
7146
7401
|
AGENT_TIMEOUT: (detail) => `The workflow agent timed out: ${detail}.`,
|
|
7147
7402
|
AGENT_FAILED: (detail) => `The workflow agent failed: ${detail}.`,
|
|
7148
7403
|
AGENT_RESULT_COLLECTED: (detail) => `The nested agent result was already collected: ${detail}.`,
|
|
@@ -7366,7 +7621,7 @@ async function createWorkflowFailureDiagnostics(store, metadata, error, run) {
|
|
|
7366
7621
|
completedSiblingAgents,
|
|
7367
7622
|
completedSiblingPaths,
|
|
7368
7623
|
...retry ? { retry } : {},
|
|
7369
|
-
artifacts: { runDirectory: store.directory, statePath:
|
|
7624
|
+
artifacts: { runDirectory: store.directory, statePath: join9(store.directory, "state.json"), journalPath: join9(store.directory, "journal.json") }
|
|
7370
7625
|
});
|
|
7371
7626
|
}
|
|
7372
7627
|
function formatWorkflowFailureDiagnostics(diagnostic) {
|
|
@@ -7393,7 +7648,7 @@ function formatWorkflowFailureDeliveryFallback(workflowName, runId, runDirectory
|
|
|
7393
7648
|
const code = errorCode(error) ?? "INTERNAL_ERROR";
|
|
7394
7649
|
const failedPath = workflowFailedAt(error);
|
|
7395
7650
|
const nextAction = retryable && code !== "BUDGET_EXHAUSTED" && code !== "CANCELLED" ? `; next action: workflow_retry({ runId: ${JSON.stringify(runId)} })` : "";
|
|
7396
|
-
const line = `Workflow ${deliveryPart(workflowName, 128)} failed (runId=${deliveryPart(runId, 128)}): error=${code}: ${deliveryPart(formatWorkflowFailure(error), 768)}${failedPath ? `; failed path=${deliveryPart(failedPath, 512)}` : ""}${nextAction}; artifacts: runDirectory=${deliveryPart(runDirectory, 512)} statePath=${deliveryPart(
|
|
7651
|
+
const line = `Workflow ${deliveryPart(workflowName, 128)} failed (runId=${deliveryPart(runId, 128)}): error=${code}: ${deliveryPart(formatWorkflowFailure(error), 768)}${failedPath ? `; failed path=${deliveryPart(failedPath, 512)}` : ""}${nextAction}; artifacts: runDirectory=${deliveryPart(runDirectory, 512)} statePath=${deliveryPart(join9(runDirectory, "state.json"), 512)} journalPath=${deliveryPart(join9(runDirectory, "journal.json"), 512)}`;
|
|
7397
7652
|
return Buffer.byteLength(line) <= DELIVERY_LIMIT_BYTES ? line : utf8Prefix(line, DELIVERY_LIMIT_BYTES);
|
|
7398
7653
|
}
|
|
7399
7654
|
function serializeWorkflowFailureDiagnostics(diagnostic) {
|
|
@@ -7433,7 +7688,7 @@ function persistedFailure(run, error) {
|
|
|
7433
7688
|
return { ...run, error: { code: error.code, message: error.message, ...failedAt ? { failedAt } : {} }, ...failedAt ? { failedAt } : {} };
|
|
7434
7689
|
}
|
|
7435
7690
|
function createWorkflowRecovery(deps) {
|
|
7436
|
-
const { pi, home, runs, scheduler, eventPublisher, persistRunState, projectTrusted: projectTrusted2, resumeHostContext: resumeHostContext2, ensureSessionLease, createAgentExecutor, activeSnapshotTools, frozenResourcePolicy: frozenResourcePolicy2, resolveLaunchPrologue, resumeRoles, workflowAgentHandler, shellForRun, resolveWorktree, checkpointBridge, phaseBridge, logBridge, lifecycleFor, createProviderErrorRecovery, cleanupTerminalRun, deliver: deliver2, deliverTerminal, workflowToolUpdate: workflowToolUpdate2, registry, modelSpec: modelSpec2 } = deps;
|
|
7691
|
+
const { pi, home, runs, scheduler, eventPublisher, persistRunState, projectTrusted: projectTrusted2, resumeHostContext: resumeHostContext2, ensureSessionLease, createAgentExecutor, activeSnapshotTools, frozenResourcePolicy: frozenResourcePolicy2, resolveLaunchPrologue, resumeRoles, workflowAgentHandler, shellForRun, toolForRun, resolveWorktree, checkpointBridge, phaseBridge, logBridge, lifecycleFor, createProviderErrorRecovery, cleanupTerminalRun, deliver: deliver2, deliverTerminal, workflowToolUpdate: workflowToolUpdate2, registry, modelSpec: modelSpec2 } = deps;
|
|
7437
7692
|
const coordinateRunMutation = deps.coordinateRunMutation ?? ((task) => task());
|
|
7438
7693
|
const budgetDecisionDelivery = (metadata, request) => `Workflow ${metadata.name} budget adjustment ${request.proposalId} for run ${request.runId} requires approval. Consumed usage: ${JSON.stringify(request.consumed)}. Previous limits: ${JSON.stringify(request.previous)}. Proposed limits: ${JSON.stringify(request.proposed)}. Respond with workflow_respond using proposalId ${request.proposalId}.`;
|
|
7439
7694
|
const appendBudgetDecisionEvent = async (run, request, type) => {
|
|
@@ -7505,7 +7760,7 @@ function createWorkflowRecovery(deps) {
|
|
|
7505
7760
|
run.executor.setRunContext(runContext);
|
|
7506
7761
|
await scheduler.cancelRun(run.store.runId);
|
|
7507
7762
|
await run.lifecycle.resume();
|
|
7508
|
-
const execution = runWorkflow(script, loaded.snapshot.args, withWorkflowFunctions({ shell: (command, options, signal, identity) => shellForRun(run.store, run.metadata, run.lifecycle, command, options, signal, identity), agent: workflowAgentHandler(run.store, run.metadata, run.lifecycle, run.executor, run.store.cwd, run.store.runId, roles.capture), worktree: async (owner) => resolveWorktree(run.store, run.metadata, owner), checkpoint: checkpointBridge(run.store.runId, run.store, run.metadata, foreground, hasUI ? ui : void 0), phase: phaseBridge(run.store, run.metadata, run.lifecycle), log: logBridge(run.store, run.lifecycle, run.metadata.name) }, run.store, runContext, registry, snapshot.settings.extensionSettings), controller.signal);
|
|
7763
|
+
const execution = runWorkflow(script, loaded.snapshot.args, withWorkflowFunctions({ tool: (identifier, args, signal, identity) => toolForRun(run.store, run.lifecycle, context?.toolContext, identifier, args, signal, identity), shell: (command, options, signal, identity) => shellForRun(run.store, run.metadata, run.lifecycle, command, options, signal, identity), agent: workflowAgentHandler(run.store, run.metadata, run.lifecycle, run.executor, run.store.cwd, run.store.runId, roles.capture), worktree: async (owner) => resolveWorktree(run.store, run.metadata, owner), checkpoint: checkpointBridge(run.store.runId, run.store, run.metadata, foreground, hasUI ? ui : void 0), phase: phaseBridge(run.store, run.metadata, run.lifecycle), log: logBridge(run.store, run.lifecycle, run.metadata.name) }, run.store, runContext, registry, snapshot.settings.extensionSettings), controller.signal);
|
|
7509
7764
|
run.execution = execution;
|
|
7510
7765
|
const completion = execution.result.then(async (value) => {
|
|
7511
7766
|
await scheduler.flush(run.store.runId);
|
|
@@ -7704,7 +7959,7 @@ function createWorkflowRecovery(deps) {
|
|
|
7704
7959
|
await eventPublisher.runStarted(prepared.childRun.store, prepared.childRun.metadata);
|
|
7705
7960
|
const { hasUI, ui } = recoveryUi(context);
|
|
7706
7961
|
const recoveryContext = resumeHostContext2(context);
|
|
7707
|
-
const completed = await coldResumeRun(prepared.childRun, hasUI, ui, projectTrusted2(context), { model: prepared.hostModel, modelRegistry: prepared.modelRegistry, deliveryContext: recoveryContext.deliveryContext, resolvedAliases: prepared.currentAliases, blockedAliases: prepared.blockedAliases, blockedAliasTargets: prepared.blockedAliasTargets, ...signal ? { signal } : {} }, modeOverride);
|
|
7962
|
+
const completed = await coldResumeRun(prepared.childRun, hasUI, ui, projectTrusted2(context), { model: prepared.hostModel, modelRegistry: prepared.modelRegistry, deliveryContext: recoveryContext.deliveryContext, toolContext: recoveryContext.toolContext, resolvedAliases: prepared.currentAliases, blockedAliases: prepared.blockedAliases, blockedAliasTargets: prepared.blockedAliasTargets, ...signal ? { signal } : {} }, modeOverride);
|
|
7708
7963
|
const completion = runs.get(prepared.childRun.store.runId)?.completion;
|
|
7709
7964
|
if (completion) {
|
|
7710
7965
|
childStarted = true;
|
|
@@ -7728,14 +7983,14 @@ function createWorkflowRecovery(deps) {
|
|
|
7728
7983
|
|
|
7729
7984
|
// src/host-navigator.ts
|
|
7730
7985
|
import { readFile as readFile5 } from "node:fs/promises";
|
|
7731
|
-
import { join as
|
|
7986
|
+
import { join as join13 } from "node:path";
|
|
7732
7987
|
import { SettingsManager as SettingsManager2, truncateToVisualLines as truncateToVisualLines2 } from "@earendil-works/pi-coding-agent";
|
|
7733
7988
|
|
|
7734
7989
|
// src/workflow-artifacts.ts
|
|
7735
7990
|
import { spawn as spawn2 } from "node:child_process";
|
|
7736
7991
|
import { mkdtemp, rm as rm4, writeFile as writeFile3 } from "node:fs/promises";
|
|
7737
7992
|
import { tmpdir as tmpdir2 } from "node:os";
|
|
7738
|
-
import { join as
|
|
7993
|
+
import { join as join10 } from "node:path";
|
|
7739
7994
|
function workflowScriptArtifact(script) {
|
|
7740
7995
|
return { extension: ".js", content: script };
|
|
7741
7996
|
}
|
|
@@ -7749,23 +8004,23 @@ function workflowResultArtifact(value) {
|
|
|
7749
8004
|
async function spawnWorkflowEditor(command, path) {
|
|
7750
8005
|
const [editor, ...editorArgs] = command.split(" ");
|
|
7751
8006
|
if (!editor) return null;
|
|
7752
|
-
return new Promise((
|
|
8007
|
+
return new Promise((resolve9) => {
|
|
7753
8008
|
try {
|
|
7754
8009
|
const child = spawn2(editor, [...editorArgs, path], { stdio: "inherit", shell: process.platform === "win32" });
|
|
7755
8010
|
child.once("error", () => {
|
|
7756
|
-
|
|
8011
|
+
resolve9(null);
|
|
7757
8012
|
});
|
|
7758
8013
|
child.once("close", (code) => {
|
|
7759
|
-
|
|
8014
|
+
resolve9(code);
|
|
7760
8015
|
});
|
|
7761
8016
|
} catch {
|
|
7762
|
-
|
|
8017
|
+
resolve9(null);
|
|
7763
8018
|
}
|
|
7764
8019
|
});
|
|
7765
8020
|
}
|
|
7766
8021
|
async function openWorkflowArtifact(tui, command, artifact) {
|
|
7767
|
-
const directory = await mkdtemp(
|
|
7768
|
-
const path =
|
|
8022
|
+
const directory = await mkdtemp(join10(tmpdir2(), "pi-workflow-editor-"));
|
|
8023
|
+
const path = join10(directory, `artifact${artifact.extension}`);
|
|
7769
8024
|
try {
|
|
7770
8025
|
await writeFile3(path, artifact.content, { encoding: "utf8", mode: 384 });
|
|
7771
8026
|
tui.stop();
|
|
@@ -7782,11 +8037,11 @@ async function openWorkflowArtifact(tui, command, artifact) {
|
|
|
7782
8037
|
|
|
7783
8038
|
// src/host-view.ts
|
|
7784
8039
|
import { keyHint, truncateToVisualLines } from "@earendil-works/pi-coding-agent";
|
|
7785
|
-
import { truncateToWidth as truncateToWidth2 } from "@earendil-works/pi-tui";
|
|
8040
|
+
import { truncateToWidth as truncateToWidth2, visibleWidth as visibleWidth2 } from "@earendil-works/pi-tui";
|
|
7786
8041
|
|
|
7787
8042
|
// src/background-widget.ts
|
|
7788
|
-
import { readFileSync as
|
|
7789
|
-
import { join as
|
|
8043
|
+
import { readFileSync as readFileSync6, statSync as statSync2 } from "node:fs";
|
|
8044
|
+
import { join as join11 } from "node:path";
|
|
7790
8045
|
import { isKeyRelease, Key, matchesKey, truncateToWidth, visibleWidth } from "@earendil-works/pi-tui";
|
|
7791
8046
|
var KEY = "piewf-widget";
|
|
7792
8047
|
var ENTRY_TYPE = "piewf-run-receipt";
|
|
@@ -7876,7 +8131,7 @@ function renderableRun(value) {
|
|
|
7876
8131
|
}
|
|
7877
8132
|
function readRun(directory) {
|
|
7878
8133
|
try {
|
|
7879
|
-
const state = JSON.parse(
|
|
8134
|
+
const state = JSON.parse(readFileSync6(join11(directory, "state.json"), "utf8"));
|
|
7880
8135
|
if (!renderableRun(state)) return void 0;
|
|
7881
8136
|
const startedAt = statSync2(directory).birthtimeMs;
|
|
7882
8137
|
return { ...state, directory, startedAt };
|
|
@@ -8105,7 +8360,6 @@ function renderFrame(runs, now, width, offset = 0, theme, scrolling = false, siz
|
|
|
8105
8360
|
const title = live.length === 1 ? "Workflow" : `Workflows \xB7 ${String(live.length)} runs`;
|
|
8106
8361
|
sizeOut.rows = rows.length;
|
|
8107
8362
|
const body = collapse(rows, MAX_ROWS - 2, paint, offset, scrolling);
|
|
8108
|
-
const inner = Math.max(20, width - 4);
|
|
8109
8363
|
const total = rows.length;
|
|
8110
8364
|
const shown = body.length;
|
|
8111
8365
|
const scrollbar = (index) => {
|
|
@@ -8117,6 +8371,11 @@ function renderFrame(runs, now, width, offset = 0, theme, scrolling = false, siz
|
|
|
8117
8371
|
const top = limit === 0 ? 0 : start >= limit ? shown - size : Math.floor(start / limit * (shown - size));
|
|
8118
8372
|
return index >= top && index < top + size ? "\u2588" : "\u2591";
|
|
8119
8373
|
};
|
|
8374
|
+
const hint = !__navigationForTests.enabled || maxOffset(total, MAX_ROWS - 2) === 0 ? "" : scrolling ? "\u2191/\u2193 to scroll \xB7 Esc to exit" : "Alt+O to scroll";
|
|
8375
|
+
return drawFrame(title, hint, body, width, paint, accent, scrollbar);
|
|
8376
|
+
}
|
|
8377
|
+
function drawFrame(title, hint, body, width, paint, accent, scrollbar = () => void 0) {
|
|
8378
|
+
const inner = Math.max(20, width - 4);
|
|
8120
8379
|
const lines = body.map((row, index) => {
|
|
8121
8380
|
const right = row.right ?? "";
|
|
8122
8381
|
const rightWidth = right === "" ? 0 : visibleLength(right) + 2;
|
|
@@ -8128,7 +8387,6 @@ function renderFrame(runs, now, width, offset = 0, theme, scrolling = false, siz
|
|
|
8128
8387
|
const bar = scrollbar(index);
|
|
8129
8388
|
return `${rule} ${text} ${bar === void 0 ? rule : accent(bar)}`;
|
|
8130
8389
|
});
|
|
8131
|
-
const hint = !__navigationForTests.enabled || maxOffset(total, MAX_ROWS - 2) === 0 ? "" : scrolling ? "\u2191/\u2193 to scroll \xB7 Esc to exit" : "Alt+O to scroll";
|
|
8132
8390
|
const label = ` ${title} `;
|
|
8133
8391
|
const tail = hint === "" ? "" : ` ${hint} `;
|
|
8134
8392
|
const spare = inner + 2 - visibleLength(label) - visibleLength(tail);
|
|
@@ -8507,7 +8765,7 @@ function widget(pi, enabled = true) {
|
|
|
8507
8765
|
continue;
|
|
8508
8766
|
}
|
|
8509
8767
|
try {
|
|
8510
|
-
const mtime = statSync2(
|
|
8768
|
+
const mtime = statSync2(join11(run.directory, "state.json")).mtimeMs;
|
|
8511
8769
|
if (mtime === seen.get(runId)) continue;
|
|
8512
8770
|
seen.set(runId, mtime);
|
|
8513
8771
|
const fresh = readRun(run.directory);
|
|
@@ -8624,7 +8882,7 @@ function widget(pi, enabled = true) {
|
|
|
8624
8882
|
const waiting = runs.get(runId)?.waiting;
|
|
8625
8883
|
runs.set(runId, waiting ? { ...run, waiting } : run);
|
|
8626
8884
|
try {
|
|
8627
|
-
seen.set(runId, statSync2(
|
|
8885
|
+
seen.set(runId, statSync2(join11(directory, "state.json")).mtimeMs);
|
|
8628
8886
|
} catch {
|
|
8629
8887
|
}
|
|
8630
8888
|
if (isRunTerminal(run.state) && !awaitingDedicatedReceipt.has(runId)) receipt(runId);
|
|
@@ -9014,13 +9272,22 @@ function formatShellActivity(activeShells, startedAt, spinner2, styles, now) {
|
|
|
9014
9272
|
const timing = started && startedAt !== void 0 && !Number.isNaN(started.getTime()) ? ` ${styles.dim(`started=${started.toISOString()} elapsed=${formatWorkflowRuntime(Math.max(0, now - startedAt))}`)}` : "";
|
|
9015
9273
|
return `${styles.accent(spinner2)} shell ${styles.accent("[running]")} ${styles.dim(`(${String(count)} active)`)}${timing}`;
|
|
9016
9274
|
}
|
|
9275
|
+
function formatToolActivity(run, spinner2, styles, now) {
|
|
9276
|
+
const calls = run.activeTools ?? [];
|
|
9277
|
+
if (!calls.length) return void 0;
|
|
9278
|
+
const counts = /* @__PURE__ */ new Map();
|
|
9279
|
+
for (const { name } of calls) counts.set(name, (counts.get(name) ?? 0) + 1);
|
|
9280
|
+
const names = [...counts].map(([name, count]) => count > 1 ? `${name} \xD7${String(count)}` : name).join(", ");
|
|
9281
|
+
const elapsed = formatWorkflowRuntime(Math.max(0, now - Math.min(...calls.map(({ startedAt }) => startedAt))));
|
|
9282
|
+
return `${styles.accent(spinner2)} tools ${styles.accent("[running]")} ${sanitizeDisplayText(names)} ${styles.dim(`elapsed=${elapsed}`)}`;
|
|
9283
|
+
}
|
|
9017
9284
|
function formatLogTimestamp(timestamp) {
|
|
9018
9285
|
if (timestamp === void 0 || !Number.isFinite(timestamp)) return "--:--:--";
|
|
9019
9286
|
const date = new Date(timestamp);
|
|
9020
9287
|
if (Number.isNaN(date.getTime())) return "--:--:--";
|
|
9021
9288
|
return [date.getHours(), date.getMinutes(), date.getSeconds()].map((part) => String(part).padStart(2, "0")).join(":");
|
|
9022
9289
|
}
|
|
9023
|
-
function workflowLogLines(run, styles, expanded, width, showHint = false) {
|
|
9290
|
+
function workflowLogLines(run, styles, expanded, width, showHint = false, tail = 5) {
|
|
9024
9291
|
const events = (run.events ?? []).filter((event) => event.type === "log");
|
|
9025
9292
|
if (!events.length) return [];
|
|
9026
9293
|
const indent = " ".repeat(11);
|
|
@@ -9030,7 +9297,7 @@ function workflowLogLines(run, styles, expanded, width, showHint = false) {
|
|
|
9030
9297
|
const chunks = truncateToVisualLines(message, Number.MAX_SAFE_INTEGER, Math.max(1, width - 11), 0).visualLines;
|
|
9031
9298
|
return chunks.map((chunk, chunkIndex) => `${chunkIndex === 0 ? prefix : indent}${chunk}`);
|
|
9032
9299
|
}));
|
|
9033
|
-
const visible = expanded ? visualLines : visualLines.slice(-
|
|
9300
|
+
const visible = expanded ? visualLines : visualLines.slice(-tail);
|
|
9034
9301
|
let hint = "";
|
|
9035
9302
|
if (!expanded && showHint) {
|
|
9036
9303
|
try {
|
|
@@ -9040,7 +9307,47 @@ function workflowLogLines(run, styles, expanded, width, showHint = false) {
|
|
|
9040
9307
|
}
|
|
9041
9308
|
return [expanded ? ` ${styles.muted("Logs")}` : ` ${styles.muted(`Logs${hint}`)}`, ...visible];
|
|
9042
9309
|
}
|
|
9043
|
-
|
|
9310
|
+
var ACTIVE_AGENT_STATES = /* @__PURE__ */ new Set(["running", "waiting_for_child", "retrying", "paused"]);
|
|
9311
|
+
var COMPACT_RECENT_SETTLED = 3;
|
|
9312
|
+
var COMPACT_LOG_LINES = 3;
|
|
9313
|
+
function expandHint() {
|
|
9314
|
+
try {
|
|
9315
|
+
return ` (${keyHint("app.tools.expand", "to expand")})`;
|
|
9316
|
+
} catch {
|
|
9317
|
+
return "";
|
|
9318
|
+
}
|
|
9319
|
+
}
|
|
9320
|
+
function agentEnd(agent) {
|
|
9321
|
+
return (agent.startedAt ?? 0) + (agent.durationMs ?? 0);
|
|
9322
|
+
}
|
|
9323
|
+
function phaseSummary(agents, styles) {
|
|
9324
|
+
const count = (state) => agents.filter((agent) => agent.state === state).length;
|
|
9325
|
+
const starts = agents.map((agent) => agent.startedAt).filter((value) => value !== void 0);
|
|
9326
|
+
const span = starts.length ? Math.max(...agents.map(agentEnd)) - Math.min(...starts) : 0;
|
|
9327
|
+
const accounting2 = sumAccounting(agents.map((agent) => agent.accounting ?? zeroAccounting()));
|
|
9328
|
+
const failed = count("failed");
|
|
9329
|
+
const parts = [`${String(count("completed"))} done`, ...failed ? [styles.error(`${String(failed)} failed`)] : [], ...count("cancelled") ? [`${String(count("cancelled"))} cancelled`] : [], ...span > 0 ? [formatWorkflowRuntime(span)] : [], formatTokens(accounting2.input + accounting2.output), formatCost(accounting2.cost)].filter(Boolean);
|
|
9330
|
+
return styles.muted(parts.join(" \xB7 "));
|
|
9331
|
+
}
|
|
9332
|
+
function formatWorkflowProgress(run, spinner2 = "\u25C7", styles = PLAIN_WORKFLOW_PROGRESS_STYLES, now = Date.now(), expanded = false, width, showHint = false, maxLines) {
|
|
9333
|
+
const full = workflowProgressLines(run, spinner2, styles, now, expanded, width, showHint);
|
|
9334
|
+
if (expanded || maxLines === void 0 || full.length <= maxLines) return full.join("\n");
|
|
9335
|
+
const phases = run.phaseHistory?.length ? run.phaseHistory : run.phase ? [{ phase: run.phase, afterAgent: 0 }] : [];
|
|
9336
|
+
const currentStart = Math.min(run.agents.length, phases.at(-1)?.afterAgent ?? 0);
|
|
9337
|
+
const current = run.agents.slice(currentStart);
|
|
9338
|
+
const recent = current.filter((agent) => SETTLED_AGENT_STATES.has(agent.state) && agent.state !== "failed").sort((left, right) => agentEnd(right) - agentEnd(left)).slice(0, COMPACT_RECENT_SETTLED);
|
|
9339
|
+
const candidates = [...current.filter((agent) => agent.state === "failed"), ...current.filter((agent) => ACTIVE_AGENT_STATES.has(agent.state)), ...recent];
|
|
9340
|
+
const render = (count) => workflowProgressLines(run, spinner2, styles, now, false, width, showHint, new Set(candidates.slice(0, count)));
|
|
9341
|
+
let low = 0;
|
|
9342
|
+
let high = candidates.length;
|
|
9343
|
+
while (low < high) {
|
|
9344
|
+
const middle = Math.ceil((low + high) / 2);
|
|
9345
|
+
if (render(middle).length <= maxLines) low = middle;
|
|
9346
|
+
else high = middle - 1;
|
|
9347
|
+
}
|
|
9348
|
+
return render(low).join("\n");
|
|
9349
|
+
}
|
|
9350
|
+
function workflowProgressLines(run, spinner2, styles, now, expanded, width, showHint, shown) {
|
|
9044
9351
|
const done = run.agents.filter((agent) => SETTLED_AGENT_STATES.has(agent.state)).length;
|
|
9045
9352
|
const workflowIcon = runStateGlyph(run.state, spinner2);
|
|
9046
9353
|
const iconStyle = workflowIconStyle(run.state, styles);
|
|
@@ -9054,15 +9361,19 @@ function formatWorkflowProgress(run, spinner2 = "\u25C7", styles = PLAIN_WORKFLO
|
|
|
9054
9361
|
const scopedShells = (run.activeShellsByPhase?.length ?? 0) > 0;
|
|
9055
9362
|
const shellActivity = scopedShells ? void 0 : formatShellActivity(run.activeShells, run.activeShellStartedAt, spinner2, styles, now);
|
|
9056
9363
|
if (shellActivity) lines.push(` ${shellActivity}`);
|
|
9364
|
+
const toolActivity = formatToolActivity(run, spinner2, styles, now);
|
|
9365
|
+
if (toolActivity) lines.push(` ${toolActivity}`);
|
|
9057
9366
|
const byId = new Map(run.agents.map((agent) => [agent.id, agent]));
|
|
9058
|
-
const
|
|
9059
|
-
|
|
9367
|
+
const position = new Map(run.agents.map((agent, index) => [agent, index]));
|
|
9368
|
+
const renderAgents = (agents, nested2) => renderGroupedAgents(agents, ({ agent, depth }, grouped) => {
|
|
9369
|
+
const icon = agentStateGlyph(agent.state, "\u25CF");
|
|
9060
9370
|
const indent = " ".repeat((grouped ? 2 : 1) + depth);
|
|
9061
|
-
const
|
|
9371
|
+
const idleSpinner = agent.state === "running" && !agentActivityLabel(agent) ? styles.accent(spinner2) : "";
|
|
9372
|
+
const activity = SETTLED_AGENT_STATES.has(agent.state) ? "" : [idleSpinner, formatAgentActivity(agent, spinner2, styles, now)].filter(Boolean).join(" ");
|
|
9062
9373
|
const name = grouped ? agent.label ?? agent.name : styledAgentBreadcrumb(agent, byId, styles);
|
|
9063
9374
|
const state2 = progressStyleForState(agent.state, styles);
|
|
9064
9375
|
const detail = expanded ? formatWorkflowAgentDetail(agent, now) : "";
|
|
9065
|
-
return `${indent}#${String(
|
|
9376
|
+
return `${indent}#${String((position.get(agent) ?? 0) + 1)} ${state2(icon)} ${name} ${state2(`[${agent.state}]`)}${activity ? ` ${activity}` : ""}${detail ? ` ${detail}` : ""}`;
|
|
9066
9377
|
}, run.agents, (label) => styles.muted(label)).map((line) => nested2 ? ` ${line}` : line);
|
|
9067
9378
|
const phases = run.phaseHistory?.length ? run.phaseHistory : run.phase ? [{ phase: run.phase, afterAgent: 0 }] : [];
|
|
9068
9379
|
if (scopedShells) {
|
|
@@ -9075,10 +9386,22 @@ function formatWorkflowProgress(run, spinner2 = "\u25C7", styles = PLAIN_WORKFLO
|
|
|
9075
9386
|
}
|
|
9076
9387
|
let renderedAgents = 0;
|
|
9077
9388
|
let nested = false;
|
|
9389
|
+
const segment = (agents, closed) => {
|
|
9390
|
+
if (!shown) return renderAgents(agents, nested);
|
|
9391
|
+
const listed = agents.filter((agent) => closed ? agent.state === "failed" : shown.has(agent));
|
|
9392
|
+
const hidden = closed ? [] : agents.filter((agent) => !shown.has(agent));
|
|
9393
|
+
const counts = [["queued", hidden.filter((agent) => !SETTLED_AGENT_STATES.has(agent.state) && !ACTIVE_AGENT_STATES.has(agent.state)).length], ["running", hidden.filter((agent) => ACTIVE_AGENT_STATES.has(agent.state)).length], ["done", hidden.filter((agent) => SETTLED_AGENT_STATES.has(agent.state)).length]];
|
|
9394
|
+
const summary = counts.filter(([, count]) => count > 0).map(([label, count]) => `+${String(count)} ${label}`).join(" \xB7 ");
|
|
9395
|
+
return [...renderAgents(listed, nested), ...summary ? [`${nested ? " " : " "}${styles.muted(`\u2026 ${summary}${showHint ? expandHint() : ""}`)}`] : []];
|
|
9396
|
+
};
|
|
9078
9397
|
for (const [phaseIndex, phase] of phases.entries()) {
|
|
9079
9398
|
const boundary = Math.max(renderedAgents, Math.min(run.agents.length, phase.afterAgent));
|
|
9080
|
-
|
|
9081
|
-
lines.push(` ${
|
|
9399
|
+
const before = run.agents.slice(renderedAgents, boundary);
|
|
9400
|
+
if (shown && !nested && before.length) lines.push(` ${phaseSummary(before, styles)}`);
|
|
9401
|
+
lines.push(...segment(before, true));
|
|
9402
|
+
const next = phases[phaseIndex + 1];
|
|
9403
|
+
const phaseAgents = next ? run.agents.slice(boundary, Math.max(boundary, Math.min(run.agents.length, next.afterAgent))) : [];
|
|
9404
|
+
lines.push(` ${styles.muted(`[Phase: ${phase.phase}]`)}${shown && next ? ` ${phaseSummary(phaseAgents, styles)}` : ""}`);
|
|
9082
9405
|
const phaseShell = scopedShells ? shellActivityFor(run, phaseIndex) : void 0;
|
|
9083
9406
|
if (phaseShell) {
|
|
9084
9407
|
const rendered = formatShellActivity(phaseShell.active, phaseShell.startedAt, spinner2, styles, now);
|
|
@@ -9087,9 +9410,9 @@ function formatWorkflowProgress(run, spinner2 = "\u25C7", styles = PLAIN_WORKFLO
|
|
|
9087
9410
|
renderedAgents = boundary;
|
|
9088
9411
|
nested = true;
|
|
9089
9412
|
}
|
|
9090
|
-
lines.push(...
|
|
9091
|
-
lines.push(...workflowLogLines(run, styles, expanded, width, showHint));
|
|
9092
|
-
return lines
|
|
9413
|
+
lines.push(...segment(run.agents.slice(renderedAgents), false));
|
|
9414
|
+
lines.push(...workflowLogLines(run, styles, expanded, width, showHint, shown ? COMPACT_LOG_LINES : 5));
|
|
9415
|
+
return lines;
|
|
9093
9416
|
}
|
|
9094
9417
|
var workflowSpinner = ["\u280B", "\u2819", "\u2839", "\u2838", "\u283C", "\u2834", "\u2826", "\u2827", "\u2807", "\u280F"];
|
|
9095
9418
|
var WORKFLOW_PROGRESS_REFRESH_MS = 1e3;
|
|
@@ -9331,7 +9654,9 @@ function workflowProgressBlock(run, theme, progress, refresh, invalidate, prefix
|
|
|
9331
9654
|
}
|
|
9332
9655
|
previousState = displayed.state;
|
|
9333
9656
|
const frame = displayed.state === "running" ? workflowSpinner[Math.floor(now / 80) % workflowSpinner.length] ?? "\u25C7" : "\u25C7";
|
|
9334
|
-
const
|
|
9657
|
+
const rows = freezeAt === void 0 && !terminal ? process.stdout.rows : void 0;
|
|
9658
|
+
const maxLines = rows ? Math.max(6, rows - 10) - (prefix ? prefix.split("\n").length + 1 : 0) : void 0;
|
|
9659
|
+
const progressText = formatWorkflowProgress(displayed, frame, styles, now, expanded, width, true, maxLines);
|
|
9335
9660
|
return truncateWorkflowProgress(prefix ? `${prefix}
|
|
9336
9661
|
|
|
9337
9662
|
${progressText}` : progressText, width);
|
|
@@ -9342,7 +9667,7 @@ ${progressText}` : progressText, width);
|
|
|
9342
9667
|
invalidate() {
|
|
9343
9668
|
const displayed = currentRun();
|
|
9344
9669
|
const now = Date.now();
|
|
9345
|
-
if (!progress || !refresh || displayed.state !== "running" || !displayed.agents.some((agent) => agent.state === "running") && (displayed.activeShells ?? 0) <= 0) return;
|
|
9670
|
+
if (!progress || !refresh || displayed.state !== "running" || !displayed.agents.some((agent) => agent.state === "running") && (displayed.activeShells ?? 0) <= 0 && !displayed.activeTools?.length) return;
|
|
9346
9671
|
if (progress.refresh || now - progress.lastRefreshAt < WORKFLOW_PROGRESS_REFRESH_MS) return;
|
|
9347
9672
|
progress.lastRefreshAt = now;
|
|
9348
9673
|
const inputRun = progress.inputRun;
|
|
@@ -9555,6 +9880,8 @@ function formatNavigatorDashboard(run, checkpoints, worktrees, now = Date.now())
|
|
|
9555
9880
|
const lines = [header, meta, ...formatCompactBudgetStatus(run)];
|
|
9556
9881
|
const shellActivity = formatShellActivity(run.activeShells, run.activeShellStartedAt, "\u2826", PLAIN_WORKFLOW_PROGRESS_STYLES, now);
|
|
9557
9882
|
if (shellActivity) lines.push(` ${shellActivity}`);
|
|
9883
|
+
const toolActivity = formatToolActivity(run, "\u2826", PLAIN_WORKFLOW_PROGRESS_STYLES, now);
|
|
9884
|
+
if (toolActivity) lines.push(` ${toolActivity}`);
|
|
9558
9885
|
if (run.error) lines.push(`Error: ${run.error.code}: ${run.error.message}`);
|
|
9559
9886
|
if (run.events?.length) lines.push(...run.events.filter((event) => event.type === "warning").map((event) => `Warning: ${event.message}`));
|
|
9560
9887
|
lines.push("");
|
|
@@ -9594,6 +9921,8 @@ function formatNavigatorRun(loaded, checkpoints, worktrees, now = Date.now()) {
|
|
|
9594
9921
|
];
|
|
9595
9922
|
const shellActivity = formatShellActivity(run.activeShells, run.activeShellStartedAt, "\u2826", PLAIN_WORKFLOW_PROGRESS_STYLES, now);
|
|
9596
9923
|
if (shellActivity) lines.push(` ${shellActivity}`);
|
|
9924
|
+
const toolActivity = formatToolActivity(run, "\u2826", PLAIN_WORKFLOW_PROGRESS_STYLES, now);
|
|
9925
|
+
if (toolActivity) lines.push(` ${toolActivity}`);
|
|
9597
9926
|
if (run.error) lines.push(`Run error: ${run.error.code}: ${run.error.message}`);
|
|
9598
9927
|
if (run.events?.length) lines.push(...run.events.filter((event) => event.type === "warning").map((event) => `Warning: ${event.message}`));
|
|
9599
9928
|
const aliases = snapshot.modelAliases ?? snapshot.settings.modelAliases;
|
|
@@ -9627,10 +9956,7 @@ function formatWorkflowPhaseDashboard(run, snapshot, width, selection = {}, styl
|
|
|
9627
9956
|
const model = buildWorkflowPhaseModel(run, snapshot);
|
|
9628
9957
|
const tree = buildWorkflowPhaseTree(model);
|
|
9629
9958
|
const expanded = selection.expandedNodeIds === void 0 ? workflowPhaseTreeInitialExpanded(tree) : new Set(selection.expandedNodeIds);
|
|
9630
|
-
const wrap = (text
|
|
9631
|
-
const ansiPattern = new RegExp(ANSI_SGR_SOURCE, "g");
|
|
9632
|
-
const visibleLength2 = (text) => text.replace(ansiPattern, "").length;
|
|
9633
|
-
const padTo = (text, limit) => `${text}${" ".repeat(Math.max(0, limit - visibleLength2(text)))}`;
|
|
9959
|
+
const wrap = (text) => wrapNavigatorLine(text, safeWidth);
|
|
9634
9960
|
const phaseStyle = (state) => phaseStyleForState(state, styles);
|
|
9635
9961
|
const phase = selection.phaseId ? model.phases.find((candidate) => candidate.id === selection.phaseId) : void 0;
|
|
9636
9962
|
const selectedByAgent = selection.agentId ? tree.nodes.find((node) => node.kind === "agent" && node.agentId === selection.agentId && (!selection.phaseId || node.phaseId === selection.phaseId)) : void 0;
|
|
@@ -9696,28 +10022,35 @@ function formatWorkflowPhaseDashboard(run, snapshot, width, selection = {}, styl
|
|
|
9696
10022
|
const scopedShells = (run.activeShellsByPhase?.length ?? 0) > 0;
|
|
9697
10023
|
const shellActivity = scopedShells ? void 0 : formatShellActivity(run.activeShells, run.activeShellStartedAt, "\u2826", styles, now);
|
|
9698
10024
|
if (shellActivity) lines.push(` ${shellActivity}`);
|
|
9699
|
-
const
|
|
10025
|
+
const toolActivity = formatToolActivity(run, "\u2826", styles, now);
|
|
10026
|
+
if (toolActivity) lines.push(` ${toolActivity}`);
|
|
9700
10027
|
const actionRows = () => {
|
|
9701
10028
|
const actions = selection.actions;
|
|
9702
10029
|
if (!actions) return [];
|
|
9703
10030
|
return ["", styles.bold(actions.title), ...actions.options.map((option, index) => `${index === actions.index ? "\u2192 " : " "}${index === actions.index ? styles.accent(option) : option}`)];
|
|
9704
10031
|
};
|
|
9705
|
-
const
|
|
10032
|
+
const treeRows = [styles.bold("Tree"), ...visibleNodes.length ? visibleNodes.map(treeLine) : [styles.muted("(empty)")]];
|
|
10033
|
+
lines.push(...formatNavigatorColumns(treeRows, [...details(selectedNode), ...actionRows()], safeWidth, selection));
|
|
10034
|
+
if (model.unassignedAgents?.length && !tree.nodes.some((node) => node.phaseId === "unassigned")) lines.push(...wrap(styles.muted(`Unassigned agents: ${String(model.unassignedAgents.length)}`)));
|
|
10035
|
+
return lines.flatMap((line) => wrap(line));
|
|
10036
|
+
}
|
|
10037
|
+
function wrapNavigatorLine(text, width) {
|
|
10038
|
+
return truncateToVisualLines(text, Number.MAX_SAFE_INTEGER, Math.max(1, width), 0).visualLines.map((line) => line.trimEnd());
|
|
10039
|
+
}
|
|
10040
|
+
function formatNavigatorColumns(sidebar, detail, width, layout = {}) {
|
|
10041
|
+
const safeWidth = Math.max(1, width);
|
|
9706
10042
|
if (safeWidth >= 80) {
|
|
9707
10043
|
const sidebarWidth = Math.min(42, Math.max(24, Math.floor((safeWidth - 3) * 0.38)));
|
|
9708
|
-
const
|
|
9709
|
-
const
|
|
9710
|
-
|
|
9711
|
-
|
|
9712
|
-
|
|
9713
|
-
|
|
9714
|
-
lines.push(...detailRows().flatMap((line) => wrap(line)));
|
|
9715
|
-
} else {
|
|
9716
|
-
lines.push(...renderTree(safeWidth));
|
|
9717
|
-
if (!selection.treeOnly) lines.push("", ...detailRows().flatMap((line) => wrap(line)));
|
|
10044
|
+
const left2 = sidebar.flatMap((line) => wrapNavigatorLine(line, sidebarWidth));
|
|
10045
|
+
const right = detail.flatMap((line) => wrapNavigatorLine(line, safeWidth - sidebarWidth - 3));
|
|
10046
|
+
return Array.from({ length: Math.max(left2.length, right.length) }, (_, index) => {
|
|
10047
|
+
const cell = left2[index] ?? "";
|
|
10048
|
+
return `${cell}${" ".repeat(Math.max(0, sidebarWidth - visibleWidth2(cell)))} | ${right[index] ?? ""}`;
|
|
10049
|
+
});
|
|
9718
10050
|
}
|
|
9719
|
-
if (
|
|
9720
|
-
|
|
10051
|
+
if (layout.detailsOnly) return detail.flatMap((line) => wrapNavigatorLine(line, safeWidth));
|
|
10052
|
+
const left = sidebar.flatMap((line) => wrapNavigatorLine(line, safeWidth));
|
|
10053
|
+
return layout.treeOnly ? left : [...left, ...detail.flatMap((line) => wrapNavigatorLine(line, safeWidth))];
|
|
9721
10054
|
}
|
|
9722
10055
|
function formatCheckpointReview(checkpoint) {
|
|
9723
10056
|
const context = JSON.stringify(checkpoint.context, null, 2);
|
|
@@ -9727,7 +10060,7 @@ function formatCheckpointReview(checkpoint) {
|
|
|
9727
10060
|
// src/retention.ts
|
|
9728
10061
|
import { lstat, readdir as readdir4, readFile as readFile4, stat as stat3 } from "node:fs/promises";
|
|
9729
10062
|
import { homedir as homedir6 } from "node:os";
|
|
9730
|
-
import { join as
|
|
10063
|
+
import { join as join12, resolve as resolve7 } from "node:path";
|
|
9731
10064
|
var DAY_MS = 24 * 60 * 60 * 1e3;
|
|
9732
10065
|
var REQUIRED_FILES = /* @__PURE__ */ new Set(["workflow.js", "state.json", "snapshot.json", "journal.json", "ownership.json", "worktrees.json", "borrowed-worktrees.json", "system-prompts.json"]);
|
|
9733
10066
|
var OPTIONAL_FILES = /* @__PURE__ */ new Set(["result.json", "summary.json"]);
|
|
@@ -9736,21 +10069,21 @@ async function validateInventory(store, loaded) {
|
|
|
9736
10069
|
const entries = await readdir4(store.directory, { withFileTypes: true });
|
|
9737
10070
|
for (const entry of entries) {
|
|
9738
10071
|
if (REQUIRED_DIRECTORIES.has(entry.name)) {
|
|
9739
|
-
if (!entry.isDirectory() || entry.isSymbolicLink()) throw new Error(`Run artifact is not a regular directory: ${
|
|
10072
|
+
if (!entry.isDirectory() || entry.isSymbolicLink()) throw new Error(`Run artifact is not a regular directory: ${join12(store.directory, entry.name)}`);
|
|
9740
10073
|
continue;
|
|
9741
10074
|
}
|
|
9742
|
-
if (!REQUIRED_FILES.has(entry.name) && !OPTIONAL_FILES.has(entry.name)) throw new Error(`Run inventory contains an unrecognized artifact: ${
|
|
9743
|
-
if (!entry.isFile() || entry.isSymbolicLink()) throw new Error(`Run artifact is not a regular file: ${
|
|
10075
|
+
if (!REQUIRED_FILES.has(entry.name) && !OPTIONAL_FILES.has(entry.name)) throw new Error(`Run inventory contains an unrecognized artifact: ${join12(store.directory, entry.name)}`);
|
|
10076
|
+
if (!entry.isFile() || entry.isSymbolicLink()) throw new Error(`Run artifact is not a regular file: ${join12(store.directory, entry.name)}`);
|
|
9744
10077
|
}
|
|
9745
10078
|
for (const name of REQUIRED_FILES) {
|
|
9746
|
-
const entry = await lstat(
|
|
9747
|
-
if (!entry.isFile() || entry.isSymbolicLink()) throw new Error(`Run artifact is not a regular file: ${
|
|
10079
|
+
const entry = await lstat(join12(store.directory, name));
|
|
10080
|
+
if (!entry.isFile() || entry.isSymbolicLink()) throw new Error(`Run artifact is not a regular file: ${join12(store.directory, name)}`);
|
|
9748
10081
|
}
|
|
9749
10082
|
if (loaded.run.state === "completed") {
|
|
9750
|
-
const result = await lstat(
|
|
9751
|
-
if (!result.isFile() || result.isSymbolicLink()) throw new Error(`Run artifact is not a regular file: ${
|
|
10083
|
+
const result = await lstat(join12(store.directory, "result.json"));
|
|
10084
|
+
if (!result.isFile() || result.isSymbolicLink()) throw new Error(`Run artifact is not a regular file: ${join12(store.directory, "result.json")}`);
|
|
9752
10085
|
}
|
|
9753
|
-
if (await readFile4(
|
|
10086
|
+
if (await readFile4(join12(store.directory, "workflow.js"), "utf8") !== loaded.snapshot.script) throw new Error("Persisted workflow source does not match its launch snapshot");
|
|
9754
10087
|
}
|
|
9755
10088
|
function runDependencyIds(run, borrowed) {
|
|
9756
10089
|
const dependencies = /* @__PURE__ */ new Set();
|
|
@@ -9772,7 +10105,7 @@ async function scanRun(cwd, sessionId, runId, home) {
|
|
|
9772
10105
|
await store.validateDeletionWorktrees();
|
|
9773
10106
|
await store.validateBorrowedWorktrees();
|
|
9774
10107
|
if (loaded.run.retry) await store.validateRetrySource();
|
|
9775
|
-
const stateMtimeMs = (await stat3(
|
|
10108
|
+
const stateMtimeMs = (await stat3(join12(store.directory, "state.json"))).mtimeMs;
|
|
9776
10109
|
const dependencies = runDependencyIds(loaded.run, await store.borrowedWorktrees());
|
|
9777
10110
|
if (dependencies.includes(runId)) throw new Error(`Run ${runId} depends on itself`);
|
|
9778
10111
|
return { sessionId, runId, store, run: loaded.run, stateMtimeMs, dependencies };
|
|
@@ -9827,9 +10160,9 @@ function cleanupOrder(runs, candidates) {
|
|
|
9827
10160
|
return ordered;
|
|
9828
10161
|
}
|
|
9829
10162
|
async function recheck(run) {
|
|
9830
|
-
const before = await stat3(
|
|
10163
|
+
const before = await stat3(join12(run.store.directory, "state.json"));
|
|
9831
10164
|
const loaded = await run.store.load();
|
|
9832
|
-
const after = await stat3(
|
|
10165
|
+
const after = await stat3(join12(run.store.directory, "state.json"));
|
|
9833
10166
|
if (loaded.run.id !== run.runId || !HARD_TERMINAL_RUN_STATES.has(loaded.run.state) || before.mtimeMs !== after.mtimeMs || after.mtimeMs !== run.stateMtimeMs) throw new Error(`Retention candidate ${run.runId} changed before deletion`);
|
|
9834
10167
|
}
|
|
9835
10168
|
async function releaseLeases(leases) {
|
|
@@ -9837,8 +10170,8 @@ async function releaseLeases(leases) {
|
|
|
9837
10170
|
for (const release of releases) if (release.status === "rejected") throw release.reason;
|
|
9838
10171
|
}
|
|
9839
10172
|
async function retainTerminalRuns(options) {
|
|
9840
|
-
const home =
|
|
9841
|
-
const cwd =
|
|
10173
|
+
const home = resolve7(options.home ?? homedir6());
|
|
10174
|
+
const cwd = resolve7(options.cwd);
|
|
9842
10175
|
const now = options.now ?? Date.now();
|
|
9843
10176
|
if (!Number.isFinite(now)) throw new Error("Retention start time is invalid");
|
|
9844
10177
|
if (options.retention.olderThanDays === void 0 && options.retention.maxTerminalRuns === void 0) return { candidates: [], deleted: [] };
|
|
@@ -9999,12 +10332,54 @@ var WORKFLOW_VIM_KEYS = { "tui.select.up": "k", "tui.select.down": "j", "tui.edi
|
|
|
9999
10332
|
function workflowKeyMatches(keybindings, data, binding) {
|
|
10000
10333
|
return keybindings.matches(data, binding) || WORKFLOW_VIM_KEYS[binding] === data;
|
|
10001
10334
|
}
|
|
10002
|
-
|
|
10335
|
+
var WORKFLOW_KEY_LABELS = { up: "\u2191", down: "\u2193", left: "\u2190", right: "\u2192", pageUp: "pgup", pageDown: "pgdn" };
|
|
10336
|
+
function workflowKeyLabel(keybindings, binding, fallback, labels = WORKFLOW_KEY_LABELS) {
|
|
10003
10337
|
const keys = keybindingKeys(keybindings, binding);
|
|
10004
10338
|
const configured = keys?.length ? keys.map((key) => labels[key] ?? key) : [fallback];
|
|
10005
10339
|
const vim = WORKFLOW_VIM_KEYS[binding];
|
|
10006
10340
|
return [...new Set(vim ? [...configured, vim] : configured)].join("/");
|
|
10007
10341
|
}
|
|
10342
|
+
function createInlineConfirm(requestRender) {
|
|
10343
|
+
let pending;
|
|
10344
|
+
const answer = (value) => {
|
|
10345
|
+
const current = pending;
|
|
10346
|
+
pending = void 0;
|
|
10347
|
+
current?.resolve(value);
|
|
10348
|
+
requestRender();
|
|
10349
|
+
};
|
|
10350
|
+
return {
|
|
10351
|
+
ask(title, message) {
|
|
10352
|
+
answer(false);
|
|
10353
|
+
return new Promise((resolve9) => {
|
|
10354
|
+
pending = { title, message, yes: true, resolve: resolve9 };
|
|
10355
|
+
requestRender();
|
|
10356
|
+
});
|
|
10357
|
+
},
|
|
10358
|
+
active() {
|
|
10359
|
+
return pending !== void 0;
|
|
10360
|
+
},
|
|
10361
|
+
rows(width, styles) {
|
|
10362
|
+
if (!pending) return [];
|
|
10363
|
+
const yes = pending.yes;
|
|
10364
|
+
const message = truncateToVisualLines2(pending.message, Number.MAX_SAFE_INTEGER, Math.max(1, width), 0).visualLines.map((line) => line.trimEnd());
|
|
10365
|
+
return [styles.bold(pending.title), ...message, ...["Yes", "No"].map((option) => option === "Yes" === yes ? `\u2192 ${styles.accent(option)}` : ` ${option}`)];
|
|
10366
|
+
},
|
|
10367
|
+
hint(keybindings) {
|
|
10368
|
+
return `${workflowKeyLabel(keybindings, "tui.select.up", "\u2191")}/${workflowKeyLabel(keybindings, "tui.select.down", "\u2193")} select \xB7 ${workflowKeyLabel(keybindings, "tui.select.confirm", "enter")} confirm \xB7 ${workflowKeyLabel(keybindings, "tui.select.cancel", "esc")} cancel`;
|
|
10369
|
+
},
|
|
10370
|
+
handleInput(keybindings, data) {
|
|
10371
|
+
if (!pending) return;
|
|
10372
|
+
if (workflowKeyMatches(keybindings, data, "tui.select.up") || workflowKeyMatches(keybindings, data, "tui.select.down")) {
|
|
10373
|
+
pending.yes = !pending.yes;
|
|
10374
|
+
requestRender();
|
|
10375
|
+
} else if (workflowKeyMatches(keybindings, data, "tui.select.confirm")) answer(pending.yes);
|
|
10376
|
+
else if (workflowKeyMatches(keybindings, data, "tui.select.cancel")) answer(false);
|
|
10377
|
+
},
|
|
10378
|
+
cancel() {
|
|
10379
|
+
answer(false);
|
|
10380
|
+
}
|
|
10381
|
+
};
|
|
10382
|
+
}
|
|
10008
10383
|
function registerWorkflowNavigator(deps) {
|
|
10009
10384
|
const { pi, home, clipboard, extensionAgentDir, runs, terminalRunStates, hardTerminalRunStates, ensureSessionLease, answerCheckpoint, recovery, stopWorkflowRun, moveForegroundToBackground, isForegroundAttached, liveAgents, registry, projectTrusted: projectTrusted2, resumeHostContext: resumeHostContext2, resumeSelectedWorkflow, reportBlocked, setNavigatorOpen, trajectoryProvider, coordinateRunMutation: coordinateRunMutationDependency } = deps;
|
|
10010
10385
|
const coordinateRunMutation = coordinateRunMutationDependency ?? ((task) => task());
|
|
@@ -10046,7 +10421,7 @@ function registerWorkflowNavigator(deps) {
|
|
|
10046
10421
|
const setStatus = uiHostCapabilities(ctx.ui)?.setStatus;
|
|
10047
10422
|
setStatus?.call(ctx.ui, "workflow-stop", text);
|
|
10048
10423
|
};
|
|
10049
|
-
const runAction = async (actionCommand, status = setWorkflowStatus) => {
|
|
10424
|
+
const runAction = async (actionCommand, status = setWorkflowStatus, confirm = (title, message) => confirmWithBlocked(ctx.ui, reportBlocked, title, message)) => {
|
|
10050
10425
|
const [action, runId, ...rest] = actionCommand.split(/\s+/);
|
|
10051
10426
|
try {
|
|
10052
10427
|
const run = runId ? runs.get(runId) : void 0;
|
|
@@ -10133,7 +10508,7 @@ function registerWorkflowNavigator(deps) {
|
|
|
10133
10508
|
}
|
|
10134
10509
|
if (action === "stop" && run) {
|
|
10135
10510
|
const workflowName = stored?.loaded.run.workflowName ?? run.metadata.name;
|
|
10136
|
-
if (!await
|
|
10511
|
+
if (!await confirm("Stop workflow?", `Stop workflow ${workflowName} (${run.store.runId})? This cannot be undone.`)) return "dashboard";
|
|
10137
10512
|
status(`Stopping workflow ${workflowName}...`);
|
|
10138
10513
|
await stopWorkflowRun(run.store.runId);
|
|
10139
10514
|
status(`Workflow ${run.store.runId} stopped.`);
|
|
@@ -10494,8 +10869,18 @@ Mutations are available through workflow tools.`, "info");
|
|
|
10494
10869
|
let selectedNodeId = tree.nodes[0]?.id;
|
|
10495
10870
|
let expandedNodeIds = new Set(workflowPhaseTreeInitialExpanded(tree));
|
|
10496
10871
|
const terminalRows = () => Math.max(1, tuiRows(tui) - WORKFLOW_PANEL_FOOTER_ROWS);
|
|
10497
|
-
const
|
|
10498
|
-
const
|
|
10872
|
+
const keyLabel = (binding, fallback) => workflowKeyLabel(keybindings, binding, fallback);
|
|
10873
|
+
const inlineConfirm = createInlineConfirm(() => {
|
|
10874
|
+
if (!disposed) tui.requestRender();
|
|
10875
|
+
});
|
|
10876
|
+
const confirmInDashboard = async (title, message) => {
|
|
10877
|
+
reportBlocked?.(true, title);
|
|
10878
|
+
try {
|
|
10879
|
+
return await inlineConfirm.ask(title, message);
|
|
10880
|
+
} finally {
|
|
10881
|
+
reportBlocked?.(false);
|
|
10882
|
+
}
|
|
10883
|
+
};
|
|
10499
10884
|
const progressNow = () => {
|
|
10500
10885
|
const now = Date.now();
|
|
10501
10886
|
if (hardTerminalRunStates.has(view.run.state)) {
|
|
@@ -10572,7 +10957,7 @@ Mutations are available through workflow tools.`, "info");
|
|
|
10572
10957
|
stopStatus = status;
|
|
10573
10958
|
setWorkflowStatus(status);
|
|
10574
10959
|
if (!disposed) tui.requestRender();
|
|
10575
|
-
}).then(async (outcome2) => {
|
|
10960
|
+
}, confirmInDashboard).then(async (outcome2) => {
|
|
10576
10961
|
if (outcome2 === "stopped") {
|
|
10577
10962
|
done("__stopped__");
|
|
10578
10963
|
return;
|
|
@@ -10609,7 +10994,8 @@ Mutations are available through workflow tools.`, "info");
|
|
|
10609
10994
|
const content = [...statusLines, ...phaseLines];
|
|
10610
10995
|
const rows = terminalRows();
|
|
10611
10996
|
const hintRows = rows >= 3 ? 1 : 0;
|
|
10612
|
-
const
|
|
10997
|
+
const confirmRows = inlineConfirm.rows(width, styles);
|
|
10998
|
+
const viewport = Math.max(1, rows - hintRows - confirmRows.length);
|
|
10613
10999
|
const maxOffset2 = Math.max(0, content.length - viewport);
|
|
10614
11000
|
dashboardOffset = Math.max(0, Math.min(maxOffset2, dashboardOffset));
|
|
10615
11001
|
if (actionMode) {
|
|
@@ -10630,12 +11016,16 @@ Mutations are available through workflow tools.`, "info");
|
|
|
10630
11016
|
dashboardOffset = Math.max(0, Math.min(maxOffset2, dashboardOffset));
|
|
10631
11017
|
const selectedNode = selectedNodeId ? tree.byId.get(selectedNodeId) : void 0;
|
|
10632
11018
|
const enterAction = selectedNode?.kind === "workflow" ? "run actions" : selectedNode?.kind === "agent" ? "agent actions" : selectedNode?.children.length ? "expand/collapse" : narrow ? "inspect" : "focus details";
|
|
10633
|
-
const hint = truncateToVisualLines2(theme.fg("dim", actionMode ? `${keyLabel("tui.select.up", "\u2191")}/${keyLabel("tui.select.down", "\u2193")} actions \xB7 ${keyLabel("tui.select.confirm", "enter")} run \xB7 ${keyLabel("tui.editor.cursorLeft", "\u2190")} tree \xB7 ${keyLabel("tui.select.cancel", "esc")} tree` : `${keyLabel("tui.select.up", "\u2191")}/${keyLabel("tui.select.down", "\u2193")} tree \xB7 ${keyLabel("tui.editor.cursorLeft", "\u2190")}/${keyLabel("tui.editor.cursorRight", "\u2192")} collapse/expand \xB7 ${keyLabel("tui.select.confirm", "enter")} ${enterAction} \xB7 a actions \xB7 ${keyLabel("tui.select.cancel", "esc")} ${narrow && detailsMode ? "tree" : "back"}${content.length > viewport ? ` \xB7 ${keyLabel("tui.select.pageUp", "pgup")}/${keyLabel("tui.select.pageDown", "pgdn")} scroll` : ""} \xB7 auto-refresh 1s`), Number.MAX_SAFE_INTEGER, width, 1).visualLines[0] ?? "";
|
|
10634
|
-
return [...content.slice(dashboardOffset, dashboardOffset + viewport), ...hintRows ? [hint] : []];
|
|
11019
|
+
const hint = truncateToVisualLines2(theme.fg("dim", inlineConfirm.active() ? inlineConfirm.hint(keybindings) : actionMode ? `${keyLabel("tui.select.up", "\u2191")}/${keyLabel("tui.select.down", "\u2193")} actions \xB7 ${keyLabel("tui.select.confirm", "enter")} run \xB7 ${keyLabel("tui.editor.cursorLeft", "\u2190")} tree \xB7 ${keyLabel("tui.select.cancel", "esc")} tree` : `${keyLabel("tui.select.up", "\u2191")}/${keyLabel("tui.select.down", "\u2193")} tree \xB7 ${keyLabel("tui.editor.cursorLeft", "\u2190")}/${keyLabel("tui.editor.cursorRight", "\u2192")} collapse/expand \xB7 ${keyLabel("tui.select.confirm", "enter")} ${enterAction} \xB7 a actions \xB7 ${keyLabel("tui.select.cancel", "esc")} ${narrow && detailsMode ? "tree" : "back"}${content.length > viewport ? ` \xB7 ${keyLabel("tui.select.pageUp", "pgup")}/${keyLabel("tui.select.pageDown", "pgdn")} scroll` : ""} \xB7 auto-refresh 1s`), Number.MAX_SAFE_INTEGER, width, 1).visualLines[0] ?? "";
|
|
11020
|
+
return [...content.slice(dashboardOffset, dashboardOffset + viewport), ...confirmRows, ...hintRows ? [hint] : []];
|
|
10635
11021
|
},
|
|
10636
11022
|
invalidate() {
|
|
10637
11023
|
},
|
|
10638
11024
|
handleInput(data) {
|
|
11025
|
+
if (inlineConfirm.active()) {
|
|
11026
|
+
inlineConfirm.handleInput(keybindings, data);
|
|
11027
|
+
return;
|
|
11028
|
+
}
|
|
10639
11029
|
if (stopRequested || editorRunning) return;
|
|
10640
11030
|
const narrow = renderedWidth < 80;
|
|
10641
11031
|
if (!actionMode && (data === "a" || data === "A")) {
|
|
@@ -10687,7 +11077,7 @@ Mutations are available through workflow tools.`, "info");
|
|
|
10687
11077
|
});
|
|
10688
11078
|
}
|
|
10689
11079
|
}
|
|
10690
|
-
} else if (action === "Open script in editor") void openArtifact(readFile5(
|
|
11080
|
+
} else if (action === "Open script in editor") void openArtifact(readFile5(join13(store.directory, "workflow.js"), "utf8").then(workflowScriptArtifact), "workflow script");
|
|
10691
11081
|
else if (action === "Stop") requestStop();
|
|
10692
11082
|
else done(action);
|
|
10693
11083
|
}
|
|
@@ -10755,6 +11145,7 @@ Mutations are available through workflow tools.`, "info");
|
|
|
10755
11145
|
disposed = true;
|
|
10756
11146
|
stopTimer();
|
|
10757
11147
|
setWorkflowStatus(void 0);
|
|
11148
|
+
inlineConfirm.cancel();
|
|
10758
11149
|
}
|
|
10759
11150
|
};
|
|
10760
11151
|
}) : await ctx.ui.select(view.dashboard, [...view.actions.keys(), "Back"]);
|
|
@@ -10809,8 +11200,7 @@ Mutations are available through workflow tools.`, "info");
|
|
|
10809
11200
|
const currentLayout = layout();
|
|
10810
11201
|
const maxOffset2 = Math.max(0, renderedLines.length - currentLayout.contentViewport);
|
|
10811
11202
|
offset = Math.min(offset, maxOffset2);
|
|
10812
|
-
const
|
|
10813
|
-
const keyLabel = (binding, fallback) => workflowKeyLabel(keybindings, binding, fallback, keyLabels);
|
|
11203
|
+
const keyLabel = (binding, fallback) => workflowKeyLabel(keybindings, binding, fallback);
|
|
10814
11204
|
const hint = truncateToVisualLines2(theme.fg("dim", `${keyLabel("tui.select.up", "\u2191")}/${keyLabel("tui.select.down", "\u2193")}/pgup/pgdn scroll \xB7 enter select \xB7 esc cancel`), Number.MAX_SAFE_INTEGER, width, 1).visualLines[0] ?? "";
|
|
10815
11205
|
const controls = currentLayout.compactControls ? [options.map((option, index) => `${index === selectedIndex ? "[" : " "}${option}${index === selectedIndex ? "]" : " "}`).join(" ")] : options.map((option, index) => `${index === selectedIndex ? "\u2192 " : " "}${option}`);
|
|
10816
11206
|
return [
|
|
@@ -10870,9 +11260,69 @@ Mutations are available through workflow tools.`, "info");
|
|
|
10870
11260
|
});
|
|
10871
11261
|
}
|
|
10872
11262
|
|
|
11263
|
+
// src/script-tools.ts
|
|
11264
|
+
function scriptToolExcluded(name) {
|
|
11265
|
+
return name === "workflow" || name.startsWith("workflow_") || name.startsWith("subagents_");
|
|
11266
|
+
}
|
|
11267
|
+
function scriptToolIdentifier(name) {
|
|
11268
|
+
let identifier = "";
|
|
11269
|
+
for (const char of name) identifier += (identifier === "" ? /^[A-Za-z_$]$/ : /^[A-Za-z0-9_$]$/).test(char) ? char : "_";
|
|
11270
|
+
return identifier || "_";
|
|
11271
|
+
}
|
|
11272
|
+
function scriptToolContext(ctx) {
|
|
11273
|
+
return object(ctx) && typeof ctx.executeTool === "function" && Array.isArray(ctx.tools) ? ctx : void 0;
|
|
11274
|
+
}
|
|
11275
|
+
function scriptToolIdentifiers(context) {
|
|
11276
|
+
return (context?.tools ?? []).filter(({ name }) => !scriptToolExcluded(name)).map(({ name }) => scriptToolIdentifier(name));
|
|
11277
|
+
}
|
|
11278
|
+
function scriptTool(context, identifier) {
|
|
11279
|
+
return context.tools.find(({ name }) => !scriptToolExcluded(name) && scriptToolIdentifier(name) === identifier);
|
|
11280
|
+
}
|
|
11281
|
+
function scriptToolValue(tool, outcome) {
|
|
11282
|
+
const structured = tool.outputSchema === void 0 ? void 0 : outcome.result.structuredContent;
|
|
11283
|
+
const text = outcome.result.content.flatMap((item) => item.type === "text" ? [item.text] : []).join("\n");
|
|
11284
|
+
if (structured === void 0 && outcome.isError) throw new WorkflowError("TOOL_FAILED", `${tool.name}: ${text || "failed"}`);
|
|
11285
|
+
let value;
|
|
11286
|
+
try {
|
|
11287
|
+
value = JSON.parse(JSON.stringify(structured ?? text));
|
|
11288
|
+
} catch (error) {
|
|
11289
|
+
throw new WorkflowError("TOOL_FAILED", `${tool.name} returned a result that is not JSON: ${errorText(error)}`);
|
|
11290
|
+
}
|
|
11291
|
+
if (!jsonValue(value)) throw new WorkflowError("TOOL_FAILED", `${tool.name} returned a result that is not JSON`);
|
|
11292
|
+
if (Buffer.byteLength(JSON.stringify(value)) > RPC_LIMIT_BYTES) throw new WorkflowError("TOOL_FAILED", `${tool.name} returned more than the 10 MB workflow RPC limit`);
|
|
11293
|
+
return value;
|
|
11294
|
+
}
|
|
11295
|
+
function validateScriptToolReferences(script, context) {
|
|
11296
|
+
const references = scriptToolReferences(script);
|
|
11297
|
+
if (!references?.length) return;
|
|
11298
|
+
if (!context) throw new WorkflowError("UNKNOWN_TOOL", `Workflow scripts can call session tools only when launched by the workflow tool; this launch has none for ${references.map((name) => `tools.${name}`).join(", ")}`);
|
|
11299
|
+
const identifiers = scriptToolIdentifiers(context);
|
|
11300
|
+
const unknown = references.filter((name) => !identifiers.includes(name));
|
|
11301
|
+
if (unknown.length) throw new WorkflowError("UNKNOWN_TOOL", `Workflow script calls tools this session cannot call: ${unknown.map((name) => `tools.${name}`).join(", ")}`);
|
|
11302
|
+
const ambiguous = references.filter((name) => identifiers.indexOf(name) !== identifiers.lastIndexOf(name));
|
|
11303
|
+
if (ambiguous.length) throw new WorkflowError("UNKNOWN_TOOL", `Workflow script calls tools whose names collide after normalization: ${ambiguous.map((name) => `tools.${name}`).join(", ")}`);
|
|
11304
|
+
}
|
|
11305
|
+
function describeScriptOutput(schema) {
|
|
11306
|
+
if (!object(schema) || schema.type !== "object" || !object(schema.properties)) return schema === void 0 ? void 0 : "a JSON value";
|
|
11307
|
+
const required = new Set(Array.isArray(schema.required) ? schema.required : []);
|
|
11308
|
+
return `\`{ ${Object.keys(schema.properties).map((name) => required.has(name) ? name : `${name}?`).join(", ")} }\``;
|
|
11309
|
+
}
|
|
11310
|
+
function prepareScriptToolLoadout(loadout) {
|
|
11311
|
+
if (loadout.declared.some(({ name }) => name === "codemode")) return void 0;
|
|
11312
|
+
const callable = new Set(loadout.callable.map(({ name }) => name));
|
|
11313
|
+
const descriptions = {};
|
|
11314
|
+
for (const tool of loadout.declared) {
|
|
11315
|
+
const output = callable.has(tool.name) && !scriptToolExcluded(tool.name) ? describeScriptOutput(tool.outputSchema) : void 0;
|
|
11316
|
+
if (output) descriptions[tool.name] = `${tool.description.trim()}
|
|
11317
|
+
|
|
11318
|
+
Workflow scripts: \`tools.${scriptToolIdentifier(tool.name)}(args)\` resolves to ${output}.`;
|
|
11319
|
+
}
|
|
11320
|
+
return { descriptions };
|
|
11321
|
+
}
|
|
11322
|
+
|
|
10873
11323
|
// src/changelog.ts
|
|
10874
11324
|
import { mkdir as mkdir3, readFile as readFile6 } from "node:fs/promises";
|
|
10875
|
-
import { dirname as
|
|
11325
|
+
import { dirname as dirname6, join as join14 } from "node:path";
|
|
10876
11326
|
import { fileURLToPath as fileURLToPath4 } from "node:url";
|
|
10877
11327
|
var STATE_DIRECTORY = "pi-extensible-workflows";
|
|
10878
11328
|
var STATE_FILE = "changelog-state.json";
|
|
@@ -10883,15 +11333,15 @@ function packageMetadata(value, directory) {
|
|
|
10883
11333
|
}
|
|
10884
11334
|
async function packageMetadataAt(directory) {
|
|
10885
11335
|
try {
|
|
10886
|
-
const value = JSON.parse(await readFile6(
|
|
11336
|
+
const value = JSON.parse(await readFile6(join14(directory, "package.json"), "utf8"));
|
|
10887
11337
|
return packageMetadata(value, directory);
|
|
10888
11338
|
} catch {
|
|
10889
11339
|
return void 0;
|
|
10890
11340
|
}
|
|
10891
11341
|
}
|
|
10892
11342
|
async function installedPackageMetadata() {
|
|
10893
|
-
const moduleDirectory =
|
|
10894
|
-
for (const directory of [
|
|
11343
|
+
const moduleDirectory = dirname6(fileURLToPath4(import.meta.url));
|
|
11344
|
+
for (const directory of [join14(moduleDirectory, ".."), join14(moduleDirectory, "../..")]) {
|
|
10895
11345
|
const metadata = await packageMetadataAt(directory);
|
|
10896
11346
|
if (metadata) return metadata;
|
|
10897
11347
|
}
|
|
@@ -10918,7 +11368,7 @@ ${line}` : line;
|
|
|
10918
11368
|
}
|
|
10919
11369
|
async function readChangelog(directory) {
|
|
10920
11370
|
try {
|
|
10921
|
-
return parseChangelog(await readFile6(
|
|
11371
|
+
return parseChangelog(await readFile6(join14(directory, "CHANGELOG.md"), "utf8"));
|
|
10922
11372
|
} catch {
|
|
10923
11373
|
return [];
|
|
10924
11374
|
}
|
|
@@ -10955,14 +11405,14 @@ async function showChangelogNotice(context, agentDir, packageDirectory) {
|
|
|
10955
11405
|
try {
|
|
10956
11406
|
const metadata = packageDirectory === void 0 ? await installedPackageMetadata() : await packageMetadataAt(packageDirectory);
|
|
10957
11407
|
if (!metadata) return;
|
|
10958
|
-
const statePath =
|
|
11408
|
+
const statePath = join14(agentDir, STATE_DIRECTORY, STATE_FILE);
|
|
10959
11409
|
const previousVersion = await lastNotifiedVersion(statePath);
|
|
10960
11410
|
if (previousVersion === metadata.version) return;
|
|
10961
11411
|
const entries = releaseEntries(await readChangelog(metadata.directory), metadata.version, previousVersion);
|
|
10962
11412
|
if (!entries.length) return;
|
|
10963
11413
|
ui.notify(noticeText(metadata.version, entries), "info");
|
|
10964
11414
|
try {
|
|
10965
|
-
await mkdir3(
|
|
11415
|
+
await mkdir3(dirname6(statePath), { recursive: true, mode: 448 });
|
|
10966
11416
|
await atomicJson(statePath, { lastNotifiedVersion: metadata.version });
|
|
10967
11417
|
} catch {
|
|
10968
11418
|
}
|
|
@@ -10971,11 +11421,11 @@ async function showChangelogNotice(context, agentDir, packageDirectory) {
|
|
|
10971
11421
|
}
|
|
10972
11422
|
|
|
10973
11423
|
// src/trajectory.ts
|
|
10974
|
-
import { createHash as
|
|
11424
|
+
import { createHash as createHash4 } from "node:crypto";
|
|
10975
11425
|
import { open as open2, readFile as readFile7, readdir as readdir5, stat as stat4 } from "node:fs/promises";
|
|
10976
11426
|
import { homedir as homedir7 } from "node:os";
|
|
10977
|
-
import { basename as basename5, dirname as
|
|
10978
|
-
import { createBashToolDefinition, createEditToolDefinition, createFindToolDefinition, createGrepToolDefinition, createLsToolDefinition, createReadToolDefinition, createWriteToolDefinition, DefaultPackageManager as DefaultPackageManager2, getAgentDir as
|
|
11427
|
+
import { basename as basename5, dirname as dirname7, join as join15, resolve as resolve8 } from "node:path";
|
|
11428
|
+
import { createBashToolDefinition, createEditToolDefinition, createFindToolDefinition, createGrepToolDefinition, createLsToolDefinition, createReadToolDefinition, createWriteToolDefinition, DefaultPackageManager as DefaultPackageManager2, getAgentDir as getAgentDir4, SettingsManager as SettingsManager3, DEFAULT_MAX_BYTES as DEFAULT_MAX_BYTES2 } from "@earendil-works/pi-coding-agent";
|
|
10979
11429
|
|
|
10980
11430
|
// subagents/src/contracts.ts
|
|
10981
11431
|
import { Type as Type2 } from "@earendil-works/pi-ai";
|
|
@@ -11003,7 +11453,8 @@ var SUBAGENTS_RUN_PARAMETERS = Type2.Object({
|
|
|
11003
11453
|
timeoutMs: Type2.Optional(Type2.Union([Type2.Integer({ minimum: 1, description: "Optional execution timeout in milliseconds" }), Type2.Null()]))
|
|
11004
11454
|
}, { additionalProperties: false });
|
|
11005
11455
|
var SUBAGENTS_INSPECT_PARAMETERS = Type2.Object({
|
|
11006
|
-
id: Type2.Optional(Type2.String({ description: "Subagent ID; omit to list ordered run summaries" }))
|
|
11456
|
+
id: Type2.Optional(Type2.String({ description: "Subagent ID; omit to list ordered run summaries" })),
|
|
11457
|
+
scope: Type2.Optional(Type2.Union([Type2.Literal("session"), Type2.Literal("all")], { description: "List scope when id is omitted: session (default) lists runs from the current Pi session; all lists every stored run" }))
|
|
11007
11458
|
}, { additionalProperties: false });
|
|
11008
11459
|
var SUBAGENTS_ID_PARAMETERS = Type2.Object({
|
|
11009
11460
|
id: Type2.String({ description: "Subagent ID" })
|
|
@@ -11018,6 +11469,21 @@ var SUBAGENTS_STOP_PARAMETERS = Type2.Object({
|
|
|
11018
11469
|
var SUBAGENTS_RETRY_PARAMETERS = Type2.Object({
|
|
11019
11470
|
id: Type2.String({ description: "Failed or stopped subagent ID to retry" })
|
|
11020
11471
|
}, { additionalProperties: false });
|
|
11472
|
+
var SUBAGENT_STATUS_OUTPUT = Type2.Object({
|
|
11473
|
+
id: Type2.String(),
|
|
11474
|
+
state: Type2.String(),
|
|
11475
|
+
sessionId: Type2.Optional(Type2.String()),
|
|
11476
|
+
startedAt: Type2.Optional(Type2.Number()),
|
|
11477
|
+
finishedAt: Type2.Optional(Type2.Number()),
|
|
11478
|
+
attempts: Type2.Optional(Type2.Number()),
|
|
11479
|
+
attemptDetails: Type2.Optional(Type2.Array(Type2.Unknown())),
|
|
11480
|
+
worktree: Type2.Optional(Type2.Object({ path: Type2.String(), branch: Type2.String() })),
|
|
11481
|
+
error: Type2.Optional(Type2.Object({ code: Type2.String(), message: Type2.String() })),
|
|
11482
|
+
progress: Type2.Optional(Type2.Unknown()),
|
|
11483
|
+
value: Type2.Optional(Type2.Unknown())
|
|
11484
|
+
});
|
|
11485
|
+
var SUBAGENTS_INSPECT_OUTPUT = Type2.Union([SUBAGENT_STATUS_OUTPUT, Type2.Array(Type2.Object({ id: Type2.String(), state: Type2.String() }))]);
|
|
11486
|
+
var SUBAGENTS_STEER_OUTPUT = Type2.Object({ id: Type2.String(), accepted: Type2.Boolean() });
|
|
11021
11487
|
function normalizeSubagentRunRequest(value) {
|
|
11022
11488
|
if (!Value2.Check(SUBAGENTS_RUN_PARAMETERS, value)) throw new WorkflowError("INVALID_METADATA", "Invalid subagents_run parameters");
|
|
11023
11489
|
if (typeof value.worktree === "string" && !value.worktree.trim()) throw new WorkflowError("INVALID_METADATA", "worktree name must be a non-empty string");
|
|
@@ -11193,6 +11659,18 @@ function statusValue(value) {
|
|
|
11193
11659
|
// src/trajectory.ts
|
|
11194
11660
|
var TRAJECTORY_MAX_TRANSCRIPT_BYTES = 2 * 1024 * 1024;
|
|
11195
11661
|
var TRAJECTORY_MAX_NON_TIMING_ENTRIES = 400;
|
|
11662
|
+
function subagentOutput(options) {
|
|
11663
|
+
if (options.resultTruncated) return { status: "truncated", kind: "result", path: options.resultPath, bytes: options.resultBytes ?? 0 };
|
|
11664
|
+
if (options.result !== void 0) return { status: "available", value: options.result, bytes: serializedJsonBytes(options.result) };
|
|
11665
|
+
if (options.failureTruncated) return { status: "truncated", kind: "failure", path: options.failurePath, bytes: options.failureBytes ?? 0 };
|
|
11666
|
+
if (options.failure !== void 0) return { status: "failed", code: options.failure.code, message: options.failure.message };
|
|
11667
|
+
if (options.state === "failed" || options.state === "stopped") {
|
|
11668
|
+
const error = options.statusError ?? options.attempt?.error;
|
|
11669
|
+
return { status: options.state === "stopped" ? "cancelled" : "failed", code: error?.code ?? (options.state === "stopped" ? "CANCELLED" : "AGENT_FAILED"), message: error?.message ?? `Subagent ${options.state}` };
|
|
11670
|
+
}
|
|
11671
|
+
if (options.state === "completed") return { status: "unavailable" };
|
|
11672
|
+
return { status: "pending" };
|
|
11673
|
+
}
|
|
11196
11674
|
function sessionFile(reference) {
|
|
11197
11675
|
const locator = reference?.locator;
|
|
11198
11676
|
if (!object(locator) || typeof locator.sessionFile !== "string" || !locator.sessionFile) return void 0;
|
|
@@ -11208,7 +11686,7 @@ function transcriptToolCallId(value) {
|
|
|
11208
11686
|
return void 0;
|
|
11209
11687
|
}
|
|
11210
11688
|
function isTimingTranscriptEntry(value) {
|
|
11211
|
-
return object(value) && value.type === "custom" && value.customType ===
|
|
11689
|
+
return object(value) && value.type === "custom" && value.customType === TOOL_TIMING_ENTRY_TYPE;
|
|
11212
11690
|
}
|
|
11213
11691
|
function timingToolCallId(value) {
|
|
11214
11692
|
if (!object(value) || !isTimingTranscriptEntry(value) || !object(value.data)) return void 0;
|
|
@@ -11329,21 +11807,21 @@ function withPiToolDescriptions(run) {
|
|
|
11329
11807
|
function canonicalExtensionSelector2(selector, base) {
|
|
11330
11808
|
const negated = selector.startsWith("!");
|
|
11331
11809
|
const body = negated ? selector.slice(1) : selector;
|
|
11332
|
-
if (body === "*" || body === "**" || body.startsWith("**/")) return selector;
|
|
11333
|
-
const resolved =
|
|
11810
|
+
if (body === "*" || body === "**" || body.startsWith("**/") || body.startsWith("builtin:")) return selector;
|
|
11811
|
+
const resolved = resolve8(base, body);
|
|
11334
11812
|
if (resourcePatternHasMagic(body)) return `${negated ? "!" : ""}${resolved}`;
|
|
11335
11813
|
return `${negated ? "!" : ""}${canonicalPath(resolved)}`;
|
|
11336
11814
|
}
|
|
11337
11815
|
function skillNameFromPath(path) {
|
|
11338
11816
|
const file = basename5(path);
|
|
11339
|
-
return file.toLowerCase() === "skill.md" ? basename5(
|
|
11817
|
+
return file.toLowerCase() === "skill.md" ? basename5(dirname7(path)) : file;
|
|
11340
11818
|
}
|
|
11341
11819
|
var discoveredResourcesByCwd = /* @__PURE__ */ new Map();
|
|
11342
11820
|
async function discoveredResources(cwd) {
|
|
11343
11821
|
const cached = discoveredResourcesByCwd.get(cwd);
|
|
11344
11822
|
if (cached) return cached;
|
|
11345
11823
|
const pending = (async () => {
|
|
11346
|
-
const agentDir =
|
|
11824
|
+
const agentDir = getAgentDir4();
|
|
11347
11825
|
const settingsManager = SettingsManager3.create(cwd, agentDir, { projectTrusted: false });
|
|
11348
11826
|
const packageManager = new DefaultPackageManager2({ cwd, agentDir, settingsManager });
|
|
11349
11827
|
const resolved = await packageManager.resolve();
|
|
@@ -11395,11 +11873,62 @@ function withoutAgentActivities(run) {
|
|
|
11395
11873
|
});
|
|
11396
11874
|
return agents.every((agent, index) => agent === run.agents[index]) ? run : { ...run, agents };
|
|
11397
11875
|
}
|
|
11876
|
+
function serializedJsonBytes(value) {
|
|
11877
|
+
const serialized = JSON.stringify(value);
|
|
11878
|
+
return Buffer.byteLength(serialized, "utf8");
|
|
11879
|
+
}
|
|
11880
|
+
function failedAgentOutput(agent) {
|
|
11881
|
+
const error = [...agent.attemptDetails ?? []].reverse().find((attempt) => attempt.error)?.error;
|
|
11882
|
+
const status = agent.state === "cancelled" ? "cancelled" : "failed";
|
|
11883
|
+
return { status, code: error?.code ?? (status === "cancelled" ? "CANCELLED" : "AGENT_FAILED"), message: error?.message ?? `Agent ${agent.state}` };
|
|
11884
|
+
}
|
|
11885
|
+
function operationOutput(operation) {
|
|
11886
|
+
const bytes = serializedJsonBytes(operation.value);
|
|
11887
|
+
return bytes > DEFAULT_MAX_BYTES2 ? { status: "truncated", kind: "result", bytes } : { status: "available", value: operation.value, bytes };
|
|
11888
|
+
}
|
|
11889
|
+
function outputForCall(call, operation) {
|
|
11890
|
+
if (operation !== void 0) return call.kind === "tool" ? { status: "completed" } : operationOutput(operation);
|
|
11891
|
+
if (call.error) return { status: call.error.code === "CANCELLED" ? "cancelled" : "failed", code: call.error.code, ...call.kind === "shell" ? { message: call.error.message ?? "Shell failed" } : {} };
|
|
11892
|
+
return call.finishedAt === void 0 ? { status: "pending" } : { status: "unavailable" };
|
|
11893
|
+
}
|
|
11894
|
+
function outputForAgent(agent, operation) {
|
|
11895
|
+
if (operation !== void 0) return operationOutput(operation);
|
|
11896
|
+
if (agent.state === "failed" || agent.state === "cancelled") return failedAgentOutput(agent);
|
|
11897
|
+
if (agent.state === "completed") return { status: "unavailable" };
|
|
11898
|
+
return { status: "pending" };
|
|
11899
|
+
}
|
|
11900
|
+
function applyTrajectoryAgentOutputs(run, operations) {
|
|
11901
|
+
const byPath = new Map(operations.map((operation) => [operation.path, operation]));
|
|
11902
|
+
const agents = run.agents.map((agent) => ({ ...agent, output: outputForAgent(agent, agent.resultPath === void 0 ? void 0 : byPath.get(agent.resultPath)) }));
|
|
11903
|
+
const scriptCalls = run.scriptCalls?.map((call) => {
|
|
11904
|
+
const recorded = { ...call, output: outputForCall(call, byPath.get(call.path)) };
|
|
11905
|
+
if (call.kind === "tool") {
|
|
11906
|
+
delete recorded.input;
|
|
11907
|
+
delete recorded.inputBytes;
|
|
11908
|
+
if (call.error) recorded.error = { code: call.error.code };
|
|
11909
|
+
}
|
|
11910
|
+
return recorded;
|
|
11911
|
+
});
|
|
11912
|
+
const rest = run;
|
|
11913
|
+
return { ...rest, agents, ...scriptCalls === void 0 ? {} : { scriptCalls } };
|
|
11914
|
+
}
|
|
11915
|
+
function overlayTrajectoryRun(run, overlay) {
|
|
11916
|
+
const overlaid = overlay(run);
|
|
11917
|
+
const outputs = new Map(run.agents.map((agent) => [agent.id, agent.output]));
|
|
11918
|
+
const agents = overlaid.agents.map((agent) => {
|
|
11919
|
+
const previous = outputs.get(agent.id);
|
|
11920
|
+
const output = previous?.status === "available" || previous?.status === "truncated" ? previous : outputForAgent(agent, void 0);
|
|
11921
|
+
return { ...agent, output };
|
|
11922
|
+
});
|
|
11923
|
+
return { ...overlaid, agents, ...run.scriptCalls === void 0 ? {} : { scriptCalls: run.scriptCalls } };
|
|
11924
|
+
}
|
|
11398
11925
|
async function loadTrajectoryRun(store, includeTranscripts = true) {
|
|
11399
11926
|
const value = await store.load();
|
|
11400
11927
|
const summary = await store.loadSummary().catch(() => void 0);
|
|
11401
11928
|
const prompts = await store.systemPrompts().catch(() => []);
|
|
11402
|
-
const
|
|
11929
|
+
const prepared = withoutAgentActivities(await withResolvedResources(withPiToolDescriptions(applySystemPrompts(value.run, prompts)), store.cwd));
|
|
11930
|
+
const operations = await store.replayableOperations().catch(() => []);
|
|
11931
|
+
const run = applyTrajectoryAgentOutputs(prepared, operations);
|
|
11403
11932
|
return { run, snapshot: value.snapshot, awaiting: await store.awaitingCheckpoints(), ...summary?.createdAt === void 0 ? {} : { createdAt: summary.createdAt }, transcripts: includeTranscripts ? await runTranscripts(run) : {} };
|
|
11404
11933
|
}
|
|
11405
11934
|
async function fileMtime(path) {
|
|
@@ -11408,8 +11937,8 @@ async function fileMtime(path) {
|
|
|
11408
11937
|
function createCachedRunLoader(cwd, sessionId, home, load, transcriptSignature, overlay) {
|
|
11409
11938
|
const cache = /* @__PURE__ */ new Map();
|
|
11410
11939
|
const changed = async (store, entry) => {
|
|
11411
|
-
if (await fileMtime(
|
|
11412
|
-
if (await fileMtime(
|
|
11940
|
+
if (await fileMtime(join15(store.directory, "state.json")) !== entry.stateMtimeMs) return true;
|
|
11941
|
+
if (await fileMtime(join15(store.directory, "journal.json")) !== entry.journalMtimeMs) return true;
|
|
11413
11942
|
for (const [agentId, path] of transcriptPaths(entry.value.run)) if (await transcriptSignature(path) !== entry.transcripts.get(agentId)) return true;
|
|
11414
11943
|
return false;
|
|
11415
11944
|
};
|
|
@@ -11426,10 +11955,10 @@ function createCachedRunLoader(cwd, sessionId, home, load, transcriptSignature,
|
|
|
11426
11955
|
const value = await load(store);
|
|
11427
11956
|
const transcripts = /* @__PURE__ */ new Map();
|
|
11428
11957
|
for (const [agentId, path] of transcriptPaths(value.run)) transcripts.set(agentId, await transcriptSignature(path));
|
|
11429
|
-
entry = { value, stateMtimeMs: await fileMtime(
|
|
11958
|
+
entry = { value, stateMtimeMs: await fileMtime(join15(store.directory, "state.json")) ?? 0, journalMtimeMs: await fileMtime(join15(store.directory, "journal.json")) ?? 0, transcripts };
|
|
11430
11959
|
cache.set(runId, entry);
|
|
11431
11960
|
}
|
|
11432
|
-
loaded.push(overlay ? { ...entry.value, run:
|
|
11961
|
+
loaded.push(overlay ? { ...entry.value, run: overlayTrajectoryRun(entry.value.run, overlay) } : entry.value);
|
|
11433
11962
|
} catch {
|
|
11434
11963
|
cache.delete(runId);
|
|
11435
11964
|
}
|
|
@@ -11441,7 +11970,7 @@ function createTrajectoryRunLoader(cwd, sessionId, home = homedir7(), overlay) {
|
|
|
11441
11970
|
return createCachedRunLoader(cwd, sessionId, home, (store) => loadTrajectoryRun(store), fileMtime, overlay);
|
|
11442
11971
|
}
|
|
11443
11972
|
function transcriptRevision(info) {
|
|
11444
|
-
const digest =
|
|
11973
|
+
const digest = createHash4("sha256").update(`${String(info.mtimeNs)}:${String(info.size)}`).digest();
|
|
11445
11974
|
return Math.max(1, digest.readUIntBE(0, 6));
|
|
11446
11975
|
}
|
|
11447
11976
|
async function transcriptMetadata(path) {
|
|
@@ -11512,7 +12041,7 @@ function createTrajectoryTranscriptLoader(cwd, sessionId, home = homedir7(), age
|
|
|
11512
12041
|
path = sessionFile(value.run.agents.find((agent) => agent.id === request.agentId)?.attemptDetails?.at(-1)?.session);
|
|
11513
12042
|
} else if (request.subagentId !== void 0 && request.runId === void 0 && request.agentId === void 0) {
|
|
11514
12043
|
if (!safeTrajectorySubagentId(request.subagentId)) return { status: "missing", revision: 0, entries: [], error: "Transcript not found" };
|
|
11515
|
-
const rawStatus = await readSubagentJson(
|
|
12044
|
+
const rawStatus = await readSubagentJson(join15(agentDir, "subagents", request.subagentId, "status.json"));
|
|
11516
12045
|
const status = statusValue(rawStatus);
|
|
11517
12046
|
const statusRecord = object(rawStatus) ? rawStatus : void 0;
|
|
11518
12047
|
const statusCwd = statusRecord === void 0 ? void 0 : statusRecord.cwd;
|
|
@@ -11573,13 +12102,13 @@ function withoutProgressActivity(progress) {
|
|
|
11573
12102
|
async function loadTrajectorySubagent(directory, cwd, sessionId, includeTranscript = true) {
|
|
11574
12103
|
try {
|
|
11575
12104
|
const id = basename5(directory);
|
|
11576
|
-
const status = statusValue(await readSubagentJson(
|
|
12105
|
+
const status = statusValue(await readSubagentJson(join15(directory, "status.json")));
|
|
11577
12106
|
if (!status || status.id !== id) return void 0;
|
|
11578
12107
|
if (status.sessionId !== sessionId) return "foreign";
|
|
11579
12108
|
const progress = status.progress === void 0 ? void 0 : withoutProgressActivity(status.progress);
|
|
11580
|
-
const request = normalizeSubagentRunRequest(await readSubagentJson(
|
|
11581
|
-
const resultPath =
|
|
11582
|
-
const failurePath =
|
|
12109
|
+
const request = normalizeSubagentRunRequest(await readSubagentJson(join15(directory, "request.json")));
|
|
12110
|
+
const resultPath = join15(directory, "result.json");
|
|
12111
|
+
const failurePath = join15(directory, "failure.json");
|
|
11583
12112
|
const resultBytes = await subagentFileBytes(resultPath);
|
|
11584
12113
|
const failureBytes = await subagentFileBytes(failurePath);
|
|
11585
12114
|
const resultValue = await readSubagentJson(resultPath, true, DEFAULT_MAX_BYTES2);
|
|
@@ -11619,19 +12148,20 @@ async function loadTrajectorySubagent(directory, cwd, sessionId, includeTranscri
|
|
|
11619
12148
|
...progress === void 0 ? {} : { progress },
|
|
11620
12149
|
...attempt === void 0 ? {} : { attempt },
|
|
11621
12150
|
...result === void 0 ? {} : { result },
|
|
11622
|
-
...failure === void 0 ? {} : { failure }
|
|
12151
|
+
...failure === void 0 ? {} : { failure },
|
|
12152
|
+
output: subagentOutput({ state: status.state, statusError: status.error, attempt, result: resultValue === void 0 ? void 0 : boundedSubagentJson(resultValue), resultTruncated, resultPath, resultBytes, failure: failureValue === void 0 ? void 0 : subagentErrorValue(failureValue), failureTruncated, failurePath, failureBytes })
|
|
11623
12153
|
};
|
|
11624
12154
|
} catch {
|
|
11625
12155
|
return void 0;
|
|
11626
12156
|
}
|
|
11627
12157
|
}
|
|
11628
12158
|
async function subagentCacheEntryChanged(directory, entry) {
|
|
11629
|
-
for (const file of TRAJECTORY_SUBAGENT_FILES) if (await fileMtime(
|
|
12159
|
+
for (const file of TRAJECTORY_SUBAGENT_FILES) if (await fileMtime(join15(directory, file)) !== entry.fileMtimes.get(file)) return true;
|
|
11630
12160
|
const transcriptPath = sessionFile(entry.value.attempt?.session);
|
|
11631
12161
|
return transcriptPath !== void 0 && await fileMtime(transcriptPath) !== entry.transcriptMtime;
|
|
11632
12162
|
}
|
|
11633
12163
|
function createTrajectorySubagentLoader(cwd, sessionId, agentDir, overlay, includeTranscript = true) {
|
|
11634
|
-
const root =
|
|
12164
|
+
const root = join15(agentDir, "subagents");
|
|
11635
12165
|
const cache = /* @__PURE__ */ new Map();
|
|
11636
12166
|
const negativeCache = /* @__PURE__ */ new Map();
|
|
11637
12167
|
return async () => {
|
|
@@ -11648,8 +12178,8 @@ function createTrajectorySubagentLoader(cwd, sessionId, agentDir, overlay, inclu
|
|
|
11648
12178
|
for (const id of negativeCache.keys()) if (!current.has(id)) negativeCache.delete(id);
|
|
11649
12179
|
const loaded = [];
|
|
11650
12180
|
for (const id of ids) {
|
|
11651
|
-
const directory =
|
|
11652
|
-
const statusPath =
|
|
12181
|
+
const directory = join15(root, id);
|
|
12182
|
+
const statusPath = join15(directory, "status.json");
|
|
11653
12183
|
try {
|
|
11654
12184
|
const statusMtime = await fileMtime(statusPath);
|
|
11655
12185
|
if (negativeCache.has(id) && negativeCache.get(id) === statusMtime) continue;
|
|
@@ -11667,7 +12197,7 @@ function createTrajectorySubagentLoader(cwd, sessionId, agentDir, overlay, inclu
|
|
|
11667
12197
|
continue;
|
|
11668
12198
|
}
|
|
11669
12199
|
const fileMtimes = /* @__PURE__ */ new Map();
|
|
11670
|
-
for (const file of TRAJECTORY_SUBAGENT_FILES) fileMtimes.set(file, await fileMtime(
|
|
12200
|
+
for (const file of TRAJECTORY_SUBAGENT_FILES) fileMtimes.set(file, await fileMtime(join15(directory, file)));
|
|
11671
12201
|
const transcriptPath = sessionFile(value.attempt?.session);
|
|
11672
12202
|
entry = { value, fileMtimes, transcriptMtime: transcriptPath === void 0 ? void 0 : await fileMtime(transcriptPath) };
|
|
11673
12203
|
cache.set(id, entry);
|
|
@@ -11723,6 +12253,12 @@ function mainAgentError(error) {
|
|
|
11723
12253
|
Object.assign(presented, typed);
|
|
11724
12254
|
return presented;
|
|
11725
12255
|
}
|
|
12256
|
+
function completionControlValue(result, controlRunId) {
|
|
12257
|
+
const record3 = { ...controlRunId === void 0 ? {} : { runId: controlRunId }, ...result };
|
|
12258
|
+
delete record3.run;
|
|
12259
|
+
delete record3.completion;
|
|
12260
|
+
return record3;
|
|
12261
|
+
}
|
|
11726
12262
|
function completionControlContent(result, controlRunId) {
|
|
11727
12263
|
const record3 = object(result) ? { ...result } : void 0;
|
|
11728
12264
|
if (record3 && controlRunId !== void 0 && record3.runId === void 0) record3.runId = controlRunId;
|
|
@@ -11768,7 +12304,7 @@ function formatWorkflowPreview(args) {
|
|
|
11768
12304
|
return [`workflow ${name}`, typeof args.description === "string" && args.description.trim() ? args.description.trim() : ""].filter(Boolean).join("\n");
|
|
11769
12305
|
}
|
|
11770
12306
|
var WORKFLOW_TOOL_LABEL = "Workflow";
|
|
11771
|
-
var WORKFLOW_TOOL_DESCRIPTION = "Run a deterministic JavaScript workflow with a named inline or file-backed parallel-to-summary path by default";
|
|
12307
|
+
var WORKFLOW_TOOL_DESCRIPTION = "Run a deterministic JavaScript workflow with a named inline or file-backed parallel-to-summary path by default. Scripts call this session's tools as `await tools.<name>(args)`, outside withWorktree: a tool resolves to its text unless its description gives a `tools.<name>(args)` result shape, and failures reject";
|
|
11772
12308
|
var WORKFLOW_TOOL_PROMPT_SNIPPET = "Run a deterministic, resumable JavaScript workflow. Prefer a named inline script that fans out independent work with parallel(...), awaits the keyed results before interpolating them into one summarizing agent(...), and returns. Provide exactly one of script or scriptPath and a non-empty name. Registered catalog functions are available as globals inside the script; call them there, for example return await someFunction(args). Advanced controls include registered functions, outputSchema, budgets, checkpoints, worktrees, retry/resume, CLI export, and pipelines. Runs are in the background by default; completion arrives as a follow-up message. Set foreground: true when the caller must wait for the final value. Manage runs from the interactive /workflow picker; use workflow_status, workflow_resume, workflow_retry, workflow_stop, and workflow_respond for explicit tool controls. If a foreground call detaches before its result is accepted, its terminal success or failure is promoted to one follow-up message. Foreground results include the completed run ID. Recovery inherits the source launch mode; legacy snapshots without launchMode recover in the background. Set foreground: true or false on workflow_resume/workflow_retry to override it; foreground recovery waits for terminal value and run details, while background recovery returns immediately and delivers completion or failure as a follow-up. After failure follow-ups, especially CANCELLED or interrupted runs, call workflow_status({ runId }) before recovery or replacement work, then pass its state as expectedState to workflow_retry/workflow_resume so recovery cannot act on a state that changed. Recovery map: agent(..., { retries }) reruns one agent call in the same run for transient failures; workflow_retry({ runId, expectedState?, foreground? }) replays a failed run into a child; workflow_resume({ runId, expectedState?, budget?, foreground? }) continues a budget_exhausted run; parentRunId on a new launch only borrows named worktrees and never replays or resumes.";
|
|
11773
12309
|
var WORKFLOW_TOOL_PARAMETERS = Type3.Object({
|
|
11774
12310
|
name: Type3.String({ description: "Required non-empty workflow name" }),
|
|
@@ -11783,8 +12319,22 @@ var WORKFLOW_TOOL_PARAMETERS = Type3.Object({
|
|
|
11783
12319
|
}, { additionalProperties: false });
|
|
11784
12320
|
var WORKFLOW_STATUS_PARAMETERS = Type3.Object({ runId: Type3.String({ description: "Workflow run ID visible in the current project" }) }, { additionalProperties: false });
|
|
11785
12321
|
var WORKFLOW_RETRY_PARAMETERS = Type3.Object({ runId: Type3.String({ description: "Explicit failed workflow run ID" }), expectedState: Type3.Optional(Type3.String({ description: "Persisted source state observed before recovery" })), foreground: Type3.Optional(Type3.Boolean({ description: "Override the source launch mode for this recovery" })) });
|
|
12322
|
+
var WORKFLOW_ERROR_OUTPUT = Type3.Object({ code: Type3.String(), message: Type3.String() });
|
|
12323
|
+
var WORKFLOW_LAUNCH_OUTPUT = Type3.Object({ runId: Type3.String(), state: Type3.String(), detached: Type3.Optional(Type3.Boolean()), value: Type3.Optional(Type3.Unknown()) });
|
|
12324
|
+
var WORKFLOW_STATUS_OUTPUT = Type3.Object({ runId: Type3.String(), workflowName: Type3.String(), state: Type3.String(), error: Type3.Optional(WORKFLOW_ERROR_OUTPUT), failedAt: Type3.Optional(Type3.String()), budget: Type3.Optional(Type3.Unknown()), usage: Type3.Optional(Type3.Unknown()), phase: Type3.Optional(Type3.String()), delivery: Type3.Optional(Type3.Object({ mode: Type3.String(), state: Type3.String() })), agents: Type3.Array(Type3.Object({ id: Type3.String(), label: Type3.Optional(Type3.String()), path: Type3.String(), state: Type3.String(), lastEventAt: Type3.Optional(Type3.Number()), accounting: Type3.Optional(Type3.Unknown()) })) });
|
|
12325
|
+
var WORKFLOW_STOP_OUTPUT = Type3.Object({ runId: Type3.String(), state: Type3.String(), stopped: Type3.Boolean(), reason: Type3.Optional(Type3.String()) });
|
|
12326
|
+
var WORKFLOW_RECOVERY_OUTPUT = Type3.Object({ state: Type3.String(), runId: Type3.Optional(Type3.String()), parentRunId: Type3.Optional(Type3.String()), proposalId: Type3.Optional(Type3.String()), value: Type3.Optional(Type3.Unknown()) });
|
|
12327
|
+
var WORKFLOW_RESPOND_OUTPUT = Type3.Object({ state: Type3.String(), approved: Type3.Boolean(), reason: Type3.String(), accepted: Type3.Optional(Type3.Boolean()), runId: Type3.Optional(Type3.String()), value: Type3.Optional(Type3.Unknown()) });
|
|
12328
|
+
var WORKFLOW_CATALOG_ALIAS_OUTPUT = Type3.Object({ name: Type3.String(), kind: Type3.String(), provenance: Type3.String(), version: Type3.Optional(Type3.String()), headline: Type3.Optional(Type3.String()) });
|
|
12329
|
+
var WORKFLOW_CATALOG_OUTPUT = Type3.Union([
|
|
12330
|
+
Type3.Object({ functions: Type3.Array(Type3.Object({ name: Type3.String(), description: Type3.String(), input: Type3.Unknown() })), modelAliasEntries: Type3.Optional(Type3.Array(WORKFLOW_CATALOG_ALIAS_OUTPUT)), settings: Type3.Optional(Type3.Unknown()) }),
|
|
12331
|
+
Type3.Object({ name: Type3.String(), version: Type3.String(), headline: Type3.String(), description: Type3.String(), input: Type3.Unknown(), output: Type3.Unknown() }),
|
|
12332
|
+
WORKFLOW_CATALOG_ALIAS_OUTPUT,
|
|
12333
|
+
Type3.Object({ error: Type3.Object({ code: Type3.String(), name: Type3.String(), message: Type3.String() }) })
|
|
12334
|
+
]);
|
|
11786
12335
|
function workflowToolUpdate(run) {
|
|
11787
|
-
|
|
12336
|
+
const done = run.agents.filter((agent) => SETTLED_AGENT_STATES.has(agent.state)).length;
|
|
12337
|
+
return { content: [{ type: "text", text: `Workflow: ${run.workflowName} (${String(done)}/${String(run.agents.length)} done) [${run.state}]` }], details: { runId: run.id, run } };
|
|
11788
12338
|
}
|
|
11789
12339
|
function agentWithProgress(agent, progress) {
|
|
11790
12340
|
const next = { ...agent, accounting: progress.accounting, toolCalls: progress.toolCalls };
|
|
@@ -11805,6 +12355,7 @@ function deliver(pi, content) {
|
|
|
11805
12355
|
pi.sendMessage({ customType: "workflow", content, display: true }, { deliverAs: "followUp", triggerTurn: true });
|
|
11806
12356
|
}
|
|
11807
12357
|
var WORKFLOW_WARNING_ENTRY = "workflow-warning";
|
|
12358
|
+
var PROGRESS_COALESCE_MS = 100;
|
|
11808
12359
|
function deliverWarning(pi, content) {
|
|
11809
12360
|
pi.appendEntry(WORKFLOW_WARNING_ENTRY, { message: content });
|
|
11810
12361
|
}
|
|
@@ -11863,7 +12414,42 @@ function modelInventory(root, registry) {
|
|
|
11863
12414
|
}
|
|
11864
12415
|
function resumeHostContext(ctx) {
|
|
11865
12416
|
const model = object(ctx) && object(ctx.model) && typeof ctx.model.provider === "string" && typeof ctx.model.id === "string" ? { provider: ctx.model.provider, id: ctx.model.id } : void 0;
|
|
11866
|
-
return { model, modelRegistry: contextHostCapabilities(ctx).modelRegistry, deliveryContext: completionContext(ctx) };
|
|
12417
|
+
return { model, modelRegistry: contextHostCapabilities(ctx).modelRegistry, deliveryContext: completionContext(ctx), toolContext: scriptToolContext(ctx) };
|
|
12418
|
+
}
|
|
12419
|
+
var SCRIPT_CALL_TEXT_LIMIT_BYTES = 16 * 1024;
|
|
12420
|
+
function scriptCallInput(input) {
|
|
12421
|
+
const text = JSON.stringify(input);
|
|
12422
|
+
const bytes = Buffer.byteLength(text);
|
|
12423
|
+
return bytes <= SCRIPT_CALL_TEXT_LIMIT_BYTES ? { input } : { input: utf8Prefix(text, SCRIPT_CALL_TEXT_LIMIT_BYTES), inputBytes: bytes };
|
|
12424
|
+
}
|
|
12425
|
+
function startScriptCall(run, call) {
|
|
12426
|
+
const recorded = { ...call, ...run.phase === void 0 ? {} : { phase: run.phase } };
|
|
12427
|
+
const calls = run.scriptCalls ?? [];
|
|
12428
|
+
return { ...run, scriptCalls: calls.some(({ path }) => path === call.path) ? calls.map((current) => current.path === call.path ? recorded : current) : [...calls, recorded] };
|
|
12429
|
+
}
|
|
12430
|
+
function finishScriptCall(run, path, error) {
|
|
12431
|
+
const finishedAt = Date.now();
|
|
12432
|
+
return { ...run, scriptCalls: (run.scriptCalls ?? []).map((call) => call.path === path ? { ...call, finishedAt, ...error ? { error: { code: error.code, ...call.kind === "shell" ? { message: utf8Prefix(error.message, SCRIPT_CALL_TEXT_LIMIT_BYTES) } : {} } } : {} } : call) };
|
|
12433
|
+
}
|
|
12434
|
+
function registerAliasVirtualModels(pi, names, settingsPath) {
|
|
12435
|
+
const register = object(pi) ? asFn(pi.registerVirtualModel) : void 0;
|
|
12436
|
+
if (!register) return;
|
|
12437
|
+
for (const name of names) {
|
|
12438
|
+
const definition = {
|
|
12439
|
+
provider: VIRTUAL_MODEL_PROVIDER,
|
|
12440
|
+
id: name,
|
|
12441
|
+
name: `${name} (workflow alias)`,
|
|
12442
|
+
thinkingLevels: THINKING_LEVELS,
|
|
12443
|
+
route: (request, ctx) => {
|
|
12444
|
+
const aliases = resolveWorkflowSettings(ctx.cwd, projectTrusted(ctx), settingsPath).effective.modelAliases ?? {};
|
|
12445
|
+
const target = resolveModelReference(name, aliases, new Set(ctx.modelRegistry.getAll().map((model2) => `${model2.provider}/${model2.id}`)), settingsPath);
|
|
12446
|
+
const model = ctx.modelRegistry.find(target.provider, target.model);
|
|
12447
|
+
if (!model) throw new WorkflowError("UNKNOWN_MODEL", `Unknown model alias ${name} resolved to ${target.provider}/${target.model}`);
|
|
12448
|
+
return { model, thinkingLevel: request.thinkingLevel };
|
|
12449
|
+
}
|
|
12450
|
+
};
|
|
12451
|
+
Reflect.apply(register, pi, [definition]);
|
|
12452
|
+
}
|
|
11867
12453
|
}
|
|
11868
12454
|
async function resolveLaunchAliases(registry, staticAliases, context, availableModels, knownModels, settingsPath) {
|
|
11869
12455
|
const dynamic = typeof registry.resolveModelAliases === "function" ? await registry.resolveModelAliases(context, new Set(Object.keys(staticAliases))) : {};
|
|
@@ -11882,7 +12468,7 @@ async function resolveLaunchAliases(registry, staticAliases, context, availableM
|
|
|
11882
12468
|
function workflowExtension(pi, home, clipboard = copyToClipboard, transport = localAgentTransport, agentDir, additionalSkillPaths = []) {
|
|
11883
12469
|
beginWorkflowExtensionLoading();
|
|
11884
12470
|
const registry = loadingRegistry();
|
|
11885
|
-
const extensionAgentDir = agentDir ??
|
|
12471
|
+
const extensionAgentDir = agentDir ?? getAgentDir5();
|
|
11886
12472
|
const registerEntryRenderer = piHostCapabilities(pi).registerEntryRenderer;
|
|
11887
12473
|
registerEntryRenderer?.(WORKFLOW_LOG_ENTRY, (entry) => {
|
|
11888
12474
|
const data = entry.data;
|
|
@@ -11894,10 +12480,13 @@ function workflowExtension(pi, home, clipboard = copyToClipboard, transport = lo
|
|
|
11894
12480
|
});
|
|
11895
12481
|
let backgroundWidgetEnabled = true;
|
|
11896
12482
|
try {
|
|
11897
|
-
|
|
12483
|
+
const globalSettings = loadSettings(workflowSettingsPath(extensionAgentDir));
|
|
12484
|
+
backgroundWidgetEnabled = globalSettings.backgroundWidget ?? true;
|
|
12485
|
+
registerAliasVirtualModels(pi, Object.keys(globalSettings.modelAliases ?? {}), workflowSettingsPath(extensionAgentDir));
|
|
11898
12486
|
} catch {
|
|
11899
12487
|
}
|
|
11900
12488
|
const backgroundWidgetController = widget(pi, backgroundWidgetEnabled);
|
|
12489
|
+
const codemodeTools = loadCodemodeToolsSetting(extensionAgentDir);
|
|
11901
12490
|
const logBridge = (store, lifecycle, workflowName) => async (message) => {
|
|
11902
12491
|
const timestamp = Date.now();
|
|
11903
12492
|
const bounded = utf8Prefix(message, DELIVERY_LIMIT_BYTES);
|
|
@@ -11925,8 +12514,8 @@ function workflowExtension(pi, home, clipboard = copyToClipboard, transport = lo
|
|
|
11925
12514
|
};
|
|
11926
12515
|
pi.on("resources_discover", () => {
|
|
11927
12516
|
if (!pi.getActiveTools().includes("workflow")) return;
|
|
11928
|
-
const extensionDir =
|
|
11929
|
-
const skillPath = [
|
|
12517
|
+
const extensionDir = dirname8(fileURLToPath5(import.meta.url));
|
|
12518
|
+
const skillPath = [join16(extensionDir, "../skills"), join16(extensionDir, "../../skills")].find((path) => existsSync2(path));
|
|
11930
12519
|
return skillPath ? { skillPaths: [skillPath] } : void 0;
|
|
11931
12520
|
});
|
|
11932
12521
|
const runs = /* @__PURE__ */ new Map();
|
|
@@ -12064,7 +12653,6 @@ Choose what to do`, ["Retry", "Change model", "Abort workflow"]);
|
|
|
12064
12653
|
const trusted = projectTrusted(context);
|
|
12065
12654
|
const settings = resolveWorkflowSettings(cwd, trusted, workflowSettingsPath(extensionAgentDir)).effective.extensionSettings?.trajectory;
|
|
12066
12655
|
const port = settings?.port;
|
|
12067
|
-
const themes = settings?.themes ?? false;
|
|
12068
12656
|
const loadRuns = createTrajectoryRunLoader(cwd, sessionId, home, overlayLiveRun);
|
|
12069
12657
|
const loadSubagents = createTrajectorySubagentLoader(cwd, sessionId, extensionAgentDir, overlayLiveSubagent);
|
|
12070
12658
|
const loadRunsMetadata = createTrajectoryRunMetadataLoader(cwd, sessionId, home, overlayLiveRun);
|
|
@@ -12073,7 +12661,7 @@ Choose what to do`, ["Retry", "Change model", "Abort workflow"]);
|
|
|
12073
12661
|
const live = liveSubagents.get(subagentId);
|
|
12074
12662
|
return live?.status.sessionId === sessionId ? live.status.attemptDetails?.at(-1)?.session : void 0;
|
|
12075
12663
|
});
|
|
12076
|
-
return { cwd, sessionId, ...port === void 0 ? {} : { port },
|
|
12664
|
+
return { cwd, sessionId, ...port === void 0 ? {} : { port }, loadRuns, loadSubagents, loadMetadata: async () => ({ runs: await loadRunsMetadata(), subagents: await loadSubagentsMetadata() }), loadTranscript, handleAction: (request) => trajectoryAction(request, context) };
|
|
12077
12665
|
};
|
|
12078
12666
|
const withLiveActivities = (run) => liveAgents.overlay(run);
|
|
12079
12667
|
const overlayLiveRun = (run) => {
|
|
@@ -12190,7 +12778,10 @@ Choose what to do`, ["Retry", "Change model", "Abort workflow"]);
|
|
|
12190
12778
|
if (replayed) return readShellResult(replayed.value);
|
|
12191
12779
|
const shellStartedAt = Date.now();
|
|
12192
12780
|
let shellPhaseIndex = -1;
|
|
12193
|
-
const
|
|
12781
|
+
const call = { kind: "shell", name: "shell", path, ...scriptCallInput({ command, ...options.timeoutMs === void 0 ? {} : { timeoutMs: options.timeoutMs }, ...options.env === void 0 ? {} : { env: Object.keys(options.env) }, ...identity.worktreeOwner ? { worktree: identity.worktreeOwner } : {} }), startedAt: shellStartedAt };
|
|
12782
|
+
let failure;
|
|
12783
|
+
const started = await persistRunState(store, metadata, (run) => {
|
|
12784
|
+
const current = startScriptCall(run, call);
|
|
12194
12785
|
const history = current.phaseHistory ?? [];
|
|
12195
12786
|
if (current.phase !== void 0) shellPhaseIndex = current.phaseHistoryIndex ?? (history.length ? history.length - 1 : 0);
|
|
12196
12787
|
const phaseActivities = [...current.activeShellsByPhase ?? []];
|
|
@@ -12206,8 +12797,12 @@ Choose what to do`, ["Retry", "Change model", "Abort workflow"]);
|
|
|
12206
12797
|
if (!jsonValue(result)) fail("SHELL_FAILED", "Shell result is not JSON-compatible");
|
|
12207
12798
|
await store.complete(path, result);
|
|
12208
12799
|
return result;
|
|
12800
|
+
} catch (error) {
|
|
12801
|
+
failure = asWorkflowError(error);
|
|
12802
|
+
throw error;
|
|
12209
12803
|
} finally {
|
|
12210
|
-
const stopped = await persistRunState(store, metadata, (
|
|
12804
|
+
const stopped = await persistRunState(store, metadata, (run) => {
|
|
12805
|
+
const current = finishScriptCall(run, path, failure);
|
|
12211
12806
|
const phaseActivities = [...current.activeShellsByPhase ?? []];
|
|
12212
12807
|
const phaseActivityIndex = phaseActivities.findIndex(({ phaseIndex }) => phaseIndex === shellPhaseIndex);
|
|
12213
12808
|
const phaseActivity = phaseActivities[phaseActivityIndex];
|
|
@@ -12227,6 +12822,55 @@ Choose what to do`, ["Retry", "Change model", "Abort workflow"]);
|
|
|
12227
12822
|
await lifecycle.leave();
|
|
12228
12823
|
}
|
|
12229
12824
|
};
|
|
12825
|
+
const toolForRun = async (store, lifecycle, context, identifier, args, signal, identity) => {
|
|
12826
|
+
await lifecycle.enter();
|
|
12827
|
+
try {
|
|
12828
|
+
const path = toolIdentityPath(identifier, args, identity);
|
|
12829
|
+
const replayed = await store.replay(path);
|
|
12830
|
+
if (replayed) return replayed.value;
|
|
12831
|
+
if (!context) fail("UNKNOWN_TOOL", `tools.${identifier} needs a run launched or recovered by a Pi tool call; runs resumed from /workflow or at session start, and headless launches, only replay journaled tool calls`);
|
|
12832
|
+
const render = async () => {
|
|
12833
|
+
try {
|
|
12834
|
+
const update = runs.get(store.runId)?.update;
|
|
12835
|
+
if (update) update(workflowToolUpdate(withLiveActivities((await store.load()).run)));
|
|
12836
|
+
} catch {
|
|
12837
|
+
}
|
|
12838
|
+
};
|
|
12839
|
+
const record3 = async (update) => {
|
|
12840
|
+
try {
|
|
12841
|
+
await store.updateState(update);
|
|
12842
|
+
} catch {
|
|
12843
|
+
}
|
|
12844
|
+
};
|
|
12845
|
+
let value;
|
|
12846
|
+
try {
|
|
12847
|
+
const tool = scriptTool(context, identifier);
|
|
12848
|
+
if (!tool) fail("UNKNOWN_TOOL", `tools.${identifier} is not a tool this session can call`);
|
|
12849
|
+
const cancelled = () => signal.aborted;
|
|
12850
|
+
if (cancelled()) fail("CANCELLED", "Workflow cancelled");
|
|
12851
|
+
await record3((run) => startScriptCall(run, { kind: "tool", name: tool.name, path, startedAt: Date.now() }));
|
|
12852
|
+
const finished = liveAgents.toolStarted(store.runId, tool.name);
|
|
12853
|
+
try {
|
|
12854
|
+
await render();
|
|
12855
|
+
const outcome = await context.executeTool(tool.name, args, { signal });
|
|
12856
|
+
if (cancelled()) fail("CANCELLED", "Workflow cancelled");
|
|
12857
|
+
value = scriptToolValue(tool, outcome);
|
|
12858
|
+
} finally {
|
|
12859
|
+
finished();
|
|
12860
|
+
}
|
|
12861
|
+
} catch (error) {
|
|
12862
|
+
const failure = error instanceof WorkflowError ? error : new WorkflowError("TOOL_FAILED", `tools.${identifier}: ${errorText(error)}`);
|
|
12863
|
+
await record3((run) => finishScriptCall(run, path, failure));
|
|
12864
|
+
throw failure;
|
|
12865
|
+
}
|
|
12866
|
+
await store.complete(path, value);
|
|
12867
|
+
await record3((run) => finishScriptCall(run, path));
|
|
12868
|
+
await render();
|
|
12869
|
+
return value;
|
|
12870
|
+
} finally {
|
|
12871
|
+
await lifecycle.leave();
|
|
12872
|
+
}
|
|
12873
|
+
};
|
|
12230
12874
|
const lifecycleFor = (store, state, metadata) => new RunLifecycle(state, async (next, previous, reason) => {
|
|
12231
12875
|
const run = runs.get(store.runId);
|
|
12232
12876
|
if (!run) throw new WorkflowError("INTERNAL_ERROR", `Unknown production run: ${store.runId}`);
|
|
@@ -12246,22 +12890,48 @@ Choose what to do`, ["Retry", "Change model", "Abort workflow"]);
|
|
|
12246
12890
|
const scheduler = new FairAgentScheduler(async ({ id, runId, tuiIndex, parentId, prompt, options, signal, setSteer }) => {
|
|
12247
12891
|
const run = runs.get(runId);
|
|
12248
12892
|
if (!run) throw new WorkflowError("INTERNAL_ERROR", `Unknown production run: ${runId}`);
|
|
12893
|
+
const applyProgress = async (progress) => {
|
|
12894
|
+
let runState;
|
|
12895
|
+
if (progress.persist) {
|
|
12896
|
+
runState = await persistRunState(run.store, run.metadata, (current) => current.agents.some((agent) => agent.id === id) ? { ...current, ...run.budget.snapshot(), agents: current.agents.map((agent) => agent.id === id ? agentWithProgress(agent, progress) : agent) } : current);
|
|
12897
|
+
} else {
|
|
12898
|
+
const loaded = await run.store.loadStatus();
|
|
12899
|
+
if (!loaded.agents.some((agent) => agent.id === id)) return;
|
|
12900
|
+
runState = { ...loaded, ...run.budget.snapshot(), agents: loaded.agents.map((agent) => agent.id === id ? agentWithProgress(agent, progress) : agent) };
|
|
12901
|
+
}
|
|
12902
|
+
if (!runState.agents.some((agent) => agent.id === id)) return;
|
|
12903
|
+
liveAgents.setActivity(runId, id, progress.activity);
|
|
12904
|
+
liveAgents.setEventTime(runId, id, progress.lastEventAt);
|
|
12905
|
+
run.update?.(workflowToolUpdate(withLiveActivities(runState)));
|
|
12906
|
+
};
|
|
12907
|
+
let deferred;
|
|
12908
|
+
let deferredTimer;
|
|
12909
|
+
let appliedAt = 0;
|
|
12910
|
+
const cancelDeferredProgress = () => {
|
|
12911
|
+
if (deferredTimer) clearTimeout(deferredTimer);
|
|
12912
|
+
deferredTimer = void 0;
|
|
12913
|
+
deferred = void 0;
|
|
12914
|
+
};
|
|
12915
|
+
const onProgress = async (progress) => {
|
|
12916
|
+
const now = Date.now();
|
|
12917
|
+
if (progress.persist || !deferredTimer && now - appliedAt >= PROGRESS_COALESCE_MS) {
|
|
12918
|
+
cancelDeferredProgress();
|
|
12919
|
+
appliedAt = now;
|
|
12920
|
+
await applyProgress(progress);
|
|
12921
|
+
return;
|
|
12922
|
+
}
|
|
12923
|
+
deferred = progress;
|
|
12924
|
+
deferredTimer ??= setTimeout(() => {
|
|
12925
|
+
deferredTimer = void 0;
|
|
12926
|
+
const next = deferred;
|
|
12927
|
+
deferred = void 0;
|
|
12928
|
+
if (!next) return;
|
|
12929
|
+
appliedAt = Date.now();
|
|
12930
|
+
void applyProgress(next).catch(() => void 0);
|
|
12931
|
+
}, Math.max(0, PROGRESS_COALESCE_MS - (now - appliedAt))).unref();
|
|
12932
|
+
};
|
|
12249
12933
|
try {
|
|
12250
12934
|
const budget = run.budget.forAgent(id);
|
|
12251
|
-
const onProgress = async (progress) => {
|
|
12252
|
-
let runState;
|
|
12253
|
-
if (progress.persist) {
|
|
12254
|
-
runState = await persistRunState(run.store, run.metadata, (current) => current.agents.some((agent) => agent.id === id) ? { ...current, ...run.budget.snapshot(), agents: current.agents.map((agent) => agent.id === id ? agentWithProgress(agent, progress) : agent) } : current);
|
|
12255
|
-
} else {
|
|
12256
|
-
const loaded = await run.store.load();
|
|
12257
|
-
if (!loaded.run.agents.some((agent) => agent.id === id)) return;
|
|
12258
|
-
runState = { ...loaded.run, ...run.budget.snapshot(), agents: loaded.run.agents.map((agent) => agent.id === id ? agentWithProgress(agent, progress) : agent) };
|
|
12259
|
-
}
|
|
12260
|
-
if (!runState.agents.some((agent) => agent.id === id)) return;
|
|
12261
|
-
liveAgents.setActivity(runId, id, progress.activity);
|
|
12262
|
-
liveAgents.setEventTime(runId, id, progress.lastEventAt);
|
|
12263
|
-
run.update?.(workflowToolUpdate(withLiveActivities(runState)));
|
|
12264
|
-
};
|
|
12265
12935
|
const onAttempt = async (attempt) => {
|
|
12266
12936
|
liveAgents.setSession(runId, id, attempt.liveSession);
|
|
12267
12937
|
liveAgents.setHandoff(runId, id, attempt);
|
|
@@ -12281,6 +12951,7 @@ Choose what to do`, ["Retry", "Change model", "Abort workflow"]);
|
|
|
12281
12951
|
scheduler.cancelChildren(id);
|
|
12282
12952
|
scheduler.retry(id);
|
|
12283
12953
|
});
|
|
12954
|
+
cancelDeferredProgress();
|
|
12284
12955
|
const before = (await run.store.load()).run;
|
|
12285
12956
|
await persistAgentAttempts(run.store, id, result.attempts);
|
|
12286
12957
|
const completed = (await run.store.load()).run;
|
|
@@ -12291,6 +12962,7 @@ Choose what to do`, ["Retry", "Change model", "Abort workflow"]);
|
|
|
12291
12962
|
run.update?.(workflowToolUpdate(withLiveActivities(persisted)));
|
|
12292
12963
|
return result.value;
|
|
12293
12964
|
} catch (error) {
|
|
12965
|
+
cancelDeferredProgress();
|
|
12294
12966
|
liveAgents.setSession(runId, id);
|
|
12295
12967
|
const attempts = getAgentAttempts(error);
|
|
12296
12968
|
if (attempts?.length) {
|
|
@@ -12426,8 +13098,8 @@ Respond with workflow_respond.`);
|
|
|
12426
13098
|
const run = runs.get(runId);
|
|
12427
13099
|
await run?.lifecycle.enterAwaitingInput();
|
|
12428
13100
|
if (!alreadyAwaiting && (!isForeground() || !ui?.select)) deliverBackgroundCheckpoint(metadata.name, runId, { ...input, name: label, path });
|
|
12429
|
-
const decision = new Promise((
|
|
12430
|
-
run?.checkpointResolvers.set(path,
|
|
13101
|
+
const decision = new Promise((resolve9, reject) => {
|
|
13102
|
+
run?.checkpointResolvers.set(path, resolve9);
|
|
12431
13103
|
if (signal.aborted) reject(new WorkflowError("CANCELLED", "Workflow cancelled"));
|
|
12432
13104
|
else signal.addEventListener("abort", () => {
|
|
12433
13105
|
run?.checkpointResolvers.delete(path);
|
|
@@ -12455,16 +13127,18 @@ Respond with workflow_respond.`);
|
|
|
12455
13127
|
return decision;
|
|
12456
13128
|
};
|
|
12457
13129
|
};
|
|
12458
|
-
const registerControlTool = (name, label, description, parameters, run) => {
|
|
13130
|
+
const registerControlTool = (name, label, description, parameters, outputSchema, run) => {
|
|
12459
13131
|
pi.registerTool({
|
|
12460
13132
|
name,
|
|
12461
13133
|
label,
|
|
12462
13134
|
description,
|
|
12463
13135
|
parameters,
|
|
13136
|
+
outputSchema,
|
|
13137
|
+
...workflowToolExposure(name, codemodeTools),
|
|
12464
13138
|
async execute(_id, params, signal, _onUpdate, ctx) {
|
|
12465
13139
|
try {
|
|
12466
13140
|
const result = await run(params, signal, ctx);
|
|
12467
|
-
return { content: [{ type: "text", text: result.text }], details: result.details };
|
|
13141
|
+
return { content: [{ type: "text", text: result.text }], details: result.details, structuredContent: result.structured };
|
|
12468
13142
|
} catch (error) {
|
|
12469
13143
|
throw mainAgentError(error);
|
|
12470
13144
|
}
|
|
@@ -12482,18 +13156,21 @@ Respond with workflow_respond.`);
|
|
|
12482
13156
|
"Workflow Respond",
|
|
12483
13157
|
"Approve or reject one pending workflow checkpoint or budget decision",
|
|
12484
13158
|
Type3.Object({ runId: Type3.String(), name: Type3.Optional(Type3.String()), proposalId: Type3.Optional(Type3.String()), approved: Type3.Boolean() }, { additionalProperties: false }),
|
|
13159
|
+
WORKFLOW_RESPOND_OUTPUT,
|
|
12485
13160
|
async (params, signal, ctx) => {
|
|
12486
13161
|
if (params.proposalId) {
|
|
12487
13162
|
const result = await recovery.answerBudgetDecision(params.runId, params.proposalId, params.approved, false, ctx, signal);
|
|
12488
13163
|
if (!result) {
|
|
12489
13164
|
const denied = { state: "budget_exhausted", approved: false, reason: "proposal_not_pending" };
|
|
12490
|
-
return { text: JSON.stringify(denied), details: denied };
|
|
13165
|
+
return { text: JSON.stringify(denied), details: denied, structured: denied };
|
|
12491
13166
|
}
|
|
12492
|
-
|
|
13167
|
+
const reason = params.approved ? "approved" : "rejected";
|
|
13168
|
+
return { text: completionControlContent(result, params.runId), details: { ...result, reason }, structured: { ...completionControlValue(result, params.runId), reason } };
|
|
12493
13169
|
}
|
|
12494
13170
|
if (!params.name) throw new WorkflowError("INVALID_METADATA", "workflow_respond requires name or proposalId");
|
|
12495
13171
|
const accepted = await answerCheckpoint(params.runId, params.name, params.approved);
|
|
12496
|
-
|
|
13172
|
+
const details = { accepted, state: accepted ? "checkpoint_answered" : "not_pending", approved: params.approved, reason: "checkpoint" };
|
|
13173
|
+
return { text: accepted ? "Checkpoint response accepted." : "Checkpoint is not awaiting a response.", details, structured: details };
|
|
12497
13174
|
}
|
|
12498
13175
|
);
|
|
12499
13176
|
registerControlTool(
|
|
@@ -12501,9 +13178,10 @@ Respond with workflow_respond.`);
|
|
|
12501
13178
|
"Workflow Stop",
|
|
12502
13179
|
"Stop an active workflow run by ID",
|
|
12503
13180
|
Type3.Object({ runId: Type3.String() }, { additionalProperties: false }),
|
|
13181
|
+
WORKFLOW_STOP_OUTPUT,
|
|
12504
13182
|
async (params) => {
|
|
12505
13183
|
const result = await stopWorkflowRun(params.runId);
|
|
12506
|
-
return { text: JSON.stringify(result), details: result };
|
|
13184
|
+
return { text: JSON.stringify(result), details: result, structured: result };
|
|
12507
13185
|
}
|
|
12508
13186
|
);
|
|
12509
13187
|
registerControlTool(
|
|
@@ -12511,9 +13189,10 @@ Respond with workflow_respond.`);
|
|
|
12511
13189
|
"Workflow Status",
|
|
12512
13190
|
"Read a compact summary of a workflow run in the current project",
|
|
12513
13191
|
WORKFLOW_STATUS_PARAMETERS,
|
|
13192
|
+
WORKFLOW_STATUS_OUTPUT,
|
|
12514
13193
|
async (params, _signal, ctx) => {
|
|
12515
13194
|
const result = await workflowStatusRun(params.runId, ctx);
|
|
12516
|
-
return { text: JSON.stringify(result), details: result };
|
|
13195
|
+
return { text: JSON.stringify(result), details: result, structured: result };
|
|
12517
13196
|
}
|
|
12518
13197
|
);
|
|
12519
13198
|
let catalogRegistered = false;
|
|
@@ -12526,13 +13205,15 @@ Respond with workflow_respond.`);
|
|
|
12526
13205
|
if (!catalog.functions.length && !hasAliases && !hasSettings) return;
|
|
12527
13206
|
pi.registerTool({
|
|
12528
13207
|
name: "workflow_catalog",
|
|
13208
|
+
...workflowToolExposure("workflow_catalog", codemodeTools),
|
|
13209
|
+
outputSchema: WORKFLOW_CATALOG_OUTPUT,
|
|
12529
13210
|
label: "Workflow Catalog",
|
|
12530
13211
|
description: "List reusable workflow functions and model aliases; pass `name` to load one entry in full",
|
|
12531
13212
|
parameters: Type3.Object({ name: Type3.Optional(Type3.String({ description: "Registered function or model alias name for full detail" })) }, { additionalProperties: false }),
|
|
12532
13213
|
async execute(_id, params = {}) {
|
|
12533
13214
|
const context = { cwd, projectTrusted: trustedProject, globalSettingsPath: workflowSettingsPath(extensionAgentDir) };
|
|
12534
13215
|
const result = params.name === void 0 ? registry.catalogIndex(context) : registry.catalogDetail(params.name, context);
|
|
12535
|
-
return { content: [{ type: "text", text: JSON.stringify(result) }], details: result };
|
|
13216
|
+
return { content: [{ type: "text", text: JSON.stringify(result) }], details: result, structuredContent: result };
|
|
12536
13217
|
},
|
|
12537
13218
|
renderCall(args, theme) {
|
|
12538
13219
|
const title = theme.fg("toolTitle", theme.bold("workflow_catalog"));
|
|
@@ -12561,9 +13242,9 @@ Respond with workflow_respond.`);
|
|
|
12561
13242
|
const captured = roleCapture(store, snapshot, definitions, project);
|
|
12562
13243
|
return { definitions, capture: (role, model) => captured.capture(role, model) };
|
|
12563
13244
|
};
|
|
12564
|
-
const activeSnapshotTools = (tools, active) => active === "session" ? new Set(tools.filter((tool) => pi
|
|
13245
|
+
const activeSnapshotTools = (tools, active) => active === "session" ? new Set(tools.filter((tool) => reachableTools(pi).includes(tool) && tool !== "workflow_catalog")) : new Set(tools.filter((tool) => active.has(tool) || tool === "workflow_catalog"));
|
|
12565
13246
|
const resumeLaunchPrologue = async (input) => {
|
|
12566
|
-
const active = new Set(pi
|
|
13247
|
+
const active = new Set(reachableTools(pi).filter((tool) => !INTERNAL_WORKFLOW_TOOLS.includes(tool)));
|
|
12567
13248
|
const missing = input.snapshot.tools.filter((tool) => tool !== "workflow_catalog").find((tool) => !active.has(tool));
|
|
12568
13249
|
if (missing) throw new WorkflowError("RESUME_INCOMPATIBLE", `Required tool is unavailable: ${missing}`);
|
|
12569
13250
|
const settingsPath = workflowSettingsPath(extensionAgentDir);
|
|
@@ -12653,6 +13334,7 @@ Respond with workflow_respond.`);
|
|
|
12653
13334
|
resumeRoles,
|
|
12654
13335
|
workflowAgentHandler,
|
|
12655
13336
|
shellForRun,
|
|
13337
|
+
toolForRun,
|
|
12656
13338
|
resolveWorktree,
|
|
12657
13339
|
checkpointBridge,
|
|
12658
13340
|
phaseBridge,
|
|
@@ -12727,9 +13409,10 @@ Respond with workflow_respond.`);
|
|
|
12727
13409
|
"Workflow Retry",
|
|
12728
13410
|
"Retry a failed workflow run by replaying its completed structural operations",
|
|
12729
13411
|
WORKFLOW_RETRY_PARAMETERS,
|
|
13412
|
+
WORKFLOW_RECOVERY_OUTPUT,
|
|
12730
13413
|
async (params, signal, ctx) => {
|
|
12731
13414
|
const result = await recovery.retryWorkflowRun(params.runId, ctx, signal, params.foreground, params.expectedState);
|
|
12732
|
-
return { text: completionControlContent(result), details: result };
|
|
13415
|
+
return { text: completionControlContent(result), details: result, structured: completionControlValue(result) };
|
|
12733
13416
|
}
|
|
12734
13417
|
);
|
|
12735
13418
|
registerControlTool(
|
|
@@ -12737,15 +13420,16 @@ Respond with workflow_respond.`);
|
|
|
12737
13420
|
"Workflow Resume",
|
|
12738
13421
|
"Resume an exhausted workflow with unchanged or patched aggregate budgets",
|
|
12739
13422
|
Type3.Object({ runId: Type3.String(), expectedState: Type3.Optional(Type3.String({ description: "Persisted source state observed before recovery" })), budget: Type3.Optional(Type3.Unknown()), foreground: Type3.Optional(Type3.Boolean({ description: "Override the source launch mode for this recovery" })) }, { additionalProperties: false }),
|
|
13423
|
+
WORKFLOW_RECOVERY_OUTPUT,
|
|
12740
13424
|
async (params, signal, ctx) => {
|
|
12741
13425
|
const result = await recovery.resumeWorkflowRun(params.runId, params.budget, ctx, signal, params.foreground, true, params.expectedState);
|
|
12742
|
-
return { text: completionControlContent(result), details: result };
|
|
13426
|
+
return { text: completionControlContent(result), details: result, structured: completionControlValue(result) };
|
|
12743
13427
|
}
|
|
12744
13428
|
);
|
|
12745
13429
|
const deliverStaleTerminal = async (store, run) => {
|
|
12746
13430
|
if (!(run.delivery?.mode === "foreground" && run.delivery.state === "attached" || run.delivery?.mode === "background" && run.delivery.state === "pending")) return;
|
|
12747
13431
|
if (run.state === "completed") {
|
|
12748
|
-
const resultPath =
|
|
13432
|
+
const resultPath = join16(store.directory, "result.json");
|
|
12749
13433
|
let resultBytes = 0;
|
|
12750
13434
|
let hasResult = true;
|
|
12751
13435
|
try {
|
|
@@ -12871,16 +13555,18 @@ ${content}` };
|
|
|
12871
13555
|
description: WORKFLOW_TOOL_DESCRIPTION,
|
|
12872
13556
|
promptSnippet: WORKFLOW_TOOL_PROMPT_SNIPPET,
|
|
12873
13557
|
parameters: WORKFLOW_TOOL_PARAMETERS,
|
|
13558
|
+
outputSchema: WORKFLOW_LAUNCH_OUTPUT,
|
|
13559
|
+
prepareLoadout: prepareScriptToolLoadout,
|
|
12874
13560
|
async execute(toolCallId, params, signal, onUpdate, ctx) {
|
|
12875
13561
|
let resolveDetached;
|
|
12876
13562
|
let foregroundStore;
|
|
12877
13563
|
let completionInstalled = false;
|
|
12878
|
-
const detachedResult = params.foreground ? new Promise((
|
|
12879
|
-
resolveDetached =
|
|
13564
|
+
const detachedResult = params.foreground ? new Promise((resolve9) => {
|
|
13565
|
+
resolveDetached = resolve9;
|
|
12880
13566
|
}) : void 0;
|
|
12881
13567
|
const detachedToolResult = (run) => {
|
|
12882
13568
|
const detached = { runId: run.id, state: "running", detached: true };
|
|
12883
|
-
return { content: [{ type: "text", text: JSON.stringify(detached) }], details: { ...detached, run, preview: `Moved workflow ${run.id} to background.` } };
|
|
13569
|
+
return { content: [{ type: "text", text: JSON.stringify(detached) }], details: { ...detached, run, preview: `Moved workflow ${run.id} to background.` }, structuredContent: detached };
|
|
12884
13570
|
};
|
|
12885
13571
|
try {
|
|
12886
13572
|
const headless = object(ctx) && ctx.headless === true;
|
|
@@ -12893,7 +13579,7 @@ ${content}` };
|
|
|
12893
13579
|
const inventory = modelInventory(rootModel, modelRegistry);
|
|
12894
13580
|
const knownModels = inventory.knownModels;
|
|
12895
13581
|
const availableModels = inventory.availableModels;
|
|
12896
|
-
const rootTools = pi
|
|
13582
|
+
const rootTools = reachableTools(pi).filter((name) => !INTERNAL_WORKFLOW_TOOLS.includes(name));
|
|
12897
13583
|
const trustedProject = projectTrusted(ctx);
|
|
12898
13584
|
const launchCwd = typeof ctx.cwd === "string" ? ctx.cwd : process.cwd();
|
|
12899
13585
|
const launch = workflowLaunchSettings(launchCwd, trustedProject, settingsPath, params.concurrency);
|
|
@@ -12911,6 +13597,8 @@ ${content}` };
|
|
|
12911
13597
|
if (trustedProject && launch.resolution.project.extensionSettings !== void 0) registry.validateExtensionSettings(launch.resolution.project.extensionSettings, { source: "project", cwd: ctx.cwd, projectTrusted: true, settingsPath: launch.resolution.projectSettingsPath });
|
|
12912
13598
|
const validated = validateWorkflowLaunchWithRegistry(params, { cwd: ctx.cwd, agentDir: extensionAgentDir, projectTrusted: trustedProject, availableModels, rootTools: new Set(rootTools), modelAliases, knownModels, settingsPath, ...settings.extensionSettings === void 0 ? {} : { extensionSettings: settings.extensionSettings } }, registry);
|
|
12913
13599
|
const { script, checked, agentDefinitions, projectAgentDefinitions, roleNames } = validated;
|
|
13600
|
+
const toolContext = scriptToolContext(ctx);
|
|
13601
|
+
validateScriptToolReferences(script, toolContext);
|
|
12914
13602
|
await ensureSessionLease(ctx.cwd, ctx.sessionManager.getSessionId());
|
|
12915
13603
|
const runId = randomUUID5();
|
|
12916
13604
|
const args = params.args ?? null;
|
|
@@ -12981,7 +13669,7 @@ ${content}` };
|
|
|
12981
13669
|
runs.set(runId, runRecord);
|
|
12982
13670
|
if (params.foreground && onUpdate) onUpdate(workflowToolUpdate((await store.load()).run));
|
|
12983
13671
|
scheduler.addRun(runId, settings.concurrency, () => runs.get(runId)?.budget.checkAgentLaunch(), settings.extensionSettings);
|
|
12984
|
-
const execution = runWorkflow(script, args, withWorkflowFunctions({ shell: (command, options, signal2, identity) => shellForRun(store, checked.metadata, lifecycle, command, options, signal2, identity), agent: workflowAgentHandler(store, checked.metadata, lifecycle, executor, ctx.cwd, runId, (role, model) => capturedRoles.capture(role, model)), worktree: async (owner) => resolveWorktree(store, checked.metadata, owner), checkpoint: checkpointBridge(runId, store, checked.metadata, () => runs.get(runId)?.foreground ?? foregroundAttached, ctx.hasUI ? ctx.ui : void 0, headless), phase: phaseBridge(store, checked.metadata, lifecycle), log: logBridge(store, lifecycle, checked.metadata.name) }, store, runContext, registry, settings.extensionSettings), runController.signal);
|
|
13672
|
+
const execution = runWorkflow(script, args, withWorkflowFunctions({ tool: (identifier, toolArgs, signal2, identity) => toolForRun(store, lifecycle, toolContext, identifier, toolArgs, signal2, identity), shell: (command, options, signal2, identity) => shellForRun(store, checked.metadata, lifecycle, command, options, signal2, identity), agent: workflowAgentHandler(store, checked.metadata, lifecycle, executor, ctx.cwd, runId, (role, model) => capturedRoles.capture(role, model)), worktree: async (owner) => resolveWorktree(store, checked.metadata, owner), checkpoint: checkpointBridge(runId, store, checked.metadata, () => runs.get(runId)?.foreground ?? foregroundAttached, ctx.hasUI ? ctx.ui : void 0, headless), phase: phaseBridge(store, checked.metadata, lifecycle), log: logBridge(store, lifecycle, checked.metadata.name) }, store, runContext, registry, settings.extensionSettings), runController.signal);
|
|
12985
13673
|
runRecord.execution = execution;
|
|
12986
13674
|
await eventPublisher.runStarted(store, checked.metadata);
|
|
12987
13675
|
const finish = execution.result.then(async (value2) => {
|
|
@@ -13019,7 +13707,7 @@ ${content}` };
|
|
|
13019
13707
|
}, async (error) => {
|
|
13020
13708
|
await deliveryController.deliverTerminal(store, deliverFailureContent(error), true);
|
|
13021
13709
|
});
|
|
13022
|
-
return { content: [{ type: "text", text: JSON.stringify({ runId, state: "running" }) }], details: { runId, preview: `Started workflow ${runId}.` } };
|
|
13710
|
+
return { content: [{ type: "text", text: JSON.stringify({ runId, state: "running" }) }], details: { runId, preview: `Started workflow ${runId}.` }, structuredContent: { runId, state: "running" } };
|
|
13023
13711
|
}
|
|
13024
13712
|
void completion.then(async (result) => {
|
|
13025
13713
|
await deliveryController.deliverDetachedTerminal(toolCallId, completionContent("background", result));
|
|
@@ -13041,7 +13729,7 @@ ${content}` };
|
|
|
13041
13729
|
}
|
|
13042
13730
|
deliveryController.foregroundDeliveries.delete(toolCallId);
|
|
13043
13731
|
const run = (await store.load()).run;
|
|
13044
|
-
return { content: [{ type: "text", text: foregroundDelivery.content }, ...foregroundDelivery.inlined ? [{ type: "text", text: `Workflow run ID: ${runId}` }] : []], details: { runId, value, run } };
|
|
13732
|
+
return { content: [{ type: "text", text: foregroundDelivery.content }, ...foregroundDelivery.inlined ? [{ type: "text", text: `Workflow run ID: ${runId}` }] : []], details: { runId, value, run }, structuredContent: { runId, state: "completed", value } };
|
|
13045
13733
|
} catch (error) {
|
|
13046
13734
|
if (params.foreground && foregroundStore && completionInstalled) {
|
|
13047
13735
|
const claim = await deliveryController.claimForegroundDelivery(foregroundStore, toolCallId);
|
|
@@ -13109,8 +13797,7 @@ ${content}` };
|
|
|
13109
13797
|
state.workflowProgressComponent = workflowProgressBlock(currentProgress?.run ?? incoming, theme, currentProgress, async () => {
|
|
13110
13798
|
const active = runs.get(incoming.id);
|
|
13111
13799
|
const store = active?.store ?? new RunStore(incoming.cwd, incoming.sessionId, incoming.id, home);
|
|
13112
|
-
|
|
13113
|
-
return withLiveActivities(loaded.run);
|
|
13800
|
+
return withLiveActivities(await store.loadStatus());
|
|
13114
13801
|
}, () => {
|
|
13115
13802
|
if (state.workflowProgress === currentProgress) requestRender();
|
|
13116
13803
|
}, void 0, state.workflowProgressFrozenAt);
|
|
@@ -13122,7 +13809,7 @@ ${content}` };
|
|
|
13122
13809
|
return textBlock(isPartial ? "Workflow starting..." : runDetails?.preview ?? (content?.type === "text" ? content.text : "Workflow finished"));
|
|
13123
13810
|
}
|
|
13124
13811
|
};
|
|
13125
|
-
pi.registerTool(workflowTool);
|
|
13812
|
+
pi.registerTool({ ...workflowTool, ...workflowToolExposure(workflowTool.name, codemodeTools) });
|
|
13126
13813
|
registerWorkflowNavigator({ pi, home, clipboard, extensionAgentDir, runs, terminalRunStates, hardTerminalRunStates: HARD_TERMINAL_RUN_STATES, ensureSessionLease, coordinateRunMutation, answerCheckpoint, recovery, stopWorkflowRun, moveForegroundToBackground: deliveryController.moveForegroundToBackground, isForegroundAttached: deliveryController.isForegroundAttached, liveAgents, registry, projectTrusted, resumeHostContext, resumeSelectedWorkflow, reportBlocked: reportWorkflowBlocked, trajectoryProvider, setNavigatorOpen: (open3) => {
|
|
13127
13814
|
if (open3) backgroundWidgetController.suspend();
|
|
13128
13815
|
else backgroundWidgetController.resume();
|
|
@@ -13151,6 +13838,7 @@ ${content}` };
|
|
|
13151
13838
|
}
|
|
13152
13839
|
var LiveAgentRegistry = class {
|
|
13153
13840
|
#byRun = /* @__PURE__ */ new Map();
|
|
13841
|
+
#tools = /* @__PURE__ */ new Map();
|
|
13154
13842
|
get(runId, agentId) {
|
|
13155
13843
|
return this.#byRun.get(runId)?.get(agentId);
|
|
13156
13844
|
}
|
|
@@ -13179,19 +13867,32 @@ var LiveAgentRegistry = class {
|
|
|
13179
13867
|
setEventTime(runId, agentId, timestamp) {
|
|
13180
13868
|
if (timestamp !== void 0) this.#state(runId, agentId).lastEventAt = timestamp;
|
|
13181
13869
|
}
|
|
13870
|
+
/** Marks a script tool call in flight until the returned callback runs. */
|
|
13871
|
+
toolStarted(runId, name) {
|
|
13872
|
+
const call = { name, startedAt: Date.now() };
|
|
13873
|
+
const calls = this.#tools.get(runId) ?? /* @__PURE__ */ new Set();
|
|
13874
|
+
this.#tools.set(runId, calls.add(call));
|
|
13875
|
+
return () => {
|
|
13876
|
+
calls.delete(call);
|
|
13877
|
+
if (!calls.size && this.#tools.get(runId) === calls) this.#tools.delete(runId);
|
|
13878
|
+
};
|
|
13879
|
+
}
|
|
13182
13880
|
deleteRun(runId) {
|
|
13183
13881
|
this.#byRun.delete(runId);
|
|
13882
|
+
this.#tools.delete(runId);
|
|
13184
13883
|
}
|
|
13185
|
-
/** Overlays live activity and
|
|
13884
|
+
/** Overlays live activity, freshness, and script tool calls onto a persisted run; returns the input when nothing is live. */
|
|
13186
13885
|
overlay(run) {
|
|
13886
|
+
const tools = this.#tools.get(run.id);
|
|
13887
|
+
const withTools = tools?.size ? { ...run, activeTools: [...tools].map((call) => ({ ...call })) } : run;
|
|
13187
13888
|
const agents = this.#byRun.get(run.id);
|
|
13188
|
-
if (!agents?.size) return
|
|
13889
|
+
if (!agents?.size) return withTools;
|
|
13189
13890
|
const next = run.agents.map((agent) => {
|
|
13190
13891
|
const live = agents.get(agent.id);
|
|
13191
13892
|
if (!live || live.activity === void 0 && live.lastEventAt === void 0) return agent;
|
|
13192
13893
|
return { ...agent, ...live.activity === void 0 ? {} : { activity: live.activity }, ...live.lastEventAt === void 0 ? {} : { lastEventAt: live.lastEventAt } };
|
|
13193
13894
|
});
|
|
13194
|
-
return next.some((agent, index) => agent !== run.agents[index]) ? { ...
|
|
13895
|
+
return next.some((agent, index) => agent !== run.agents[index]) ? { ...withTools, agents: next } : withTools;
|
|
13195
13896
|
}
|
|
13196
13897
|
#state(runId, agentId) {
|
|
13197
13898
|
const agents = this.#byRun.get(runId) ?? /* @__PURE__ */ new Map();
|
|
@@ -13223,9 +13924,9 @@ async function handleTurnInput(store, handle, turn) {
|
|
|
13223
13924
|
}
|
|
13224
13925
|
if (!completed) return void 0;
|
|
13225
13926
|
if (source === void 0) fail("AGENT_FAILED", `Agent handle ${handle} cannot continue from its previous turn: the completed turn recorded no session file`);
|
|
13226
|
-
const target =
|
|
13927
|
+
const target = join16(store.directory, "handles", encodeURIComponent(handle), `turn-${String(turn)}-input.jsonl`);
|
|
13227
13928
|
try {
|
|
13228
|
-
await mkdir4(
|
|
13929
|
+
await mkdir4(dirname8(target), { recursive: true });
|
|
13229
13930
|
await copyFile2(source, target);
|
|
13230
13931
|
} catch (error) {
|
|
13231
13932
|
fail("AGENT_FAILED", `Agent handle ${handle} cannot continue from its previous turn: ${errorText(error)}`);
|
|
@@ -13246,13 +13947,13 @@ function modelSpec(value, fallback) {
|
|
|
13246
13947
|
|
|
13247
13948
|
// src/herdr.ts
|
|
13248
13949
|
import { execFile as execFile3 } from "node:child_process";
|
|
13249
|
-
var herdrCommandRunner = (args) => new Promise((
|
|
13950
|
+
var herdrCommandRunner = (args) => new Promise((resolve9, reject) => {
|
|
13250
13951
|
execFile3("herdr", [...args], { encoding: "utf8", maxBuffer: 1024 * 1024 }, (error, stdout) => {
|
|
13251
13952
|
if (error) {
|
|
13252
13953
|
reject(new Error(error.message));
|
|
13253
13954
|
return;
|
|
13254
13955
|
}
|
|
13255
|
-
|
|
13956
|
+
resolve9(stdout);
|
|
13256
13957
|
});
|
|
13257
13958
|
});
|
|
13258
13959
|
function herdrPaneId(env = process.env) {
|
|
@@ -13384,9 +14085,9 @@ async function waitForHerdrPane(paneId, runner = herdrCommandRunner, options = {
|
|
|
13384
14085
|
const isAborted = () => monitorController.signal.aborted;
|
|
13385
14086
|
let markAborted = () => {
|
|
13386
14087
|
};
|
|
13387
|
-
const aborted = new Promise((
|
|
14088
|
+
const aborted = new Promise((resolve9) => {
|
|
13388
14089
|
markAborted = () => {
|
|
13389
|
-
|
|
14090
|
+
resolve9("aborted");
|
|
13390
14091
|
};
|
|
13391
14092
|
});
|
|
13392
14093
|
const abortMonitor = () => {
|
|
@@ -13395,16 +14096,16 @@ async function waitForHerdrPane(paneId, runner = herdrCommandRunner, options = {
|
|
|
13395
14096
|
};
|
|
13396
14097
|
if (options.signal?.aborted) abortMonitor();
|
|
13397
14098
|
else options.signal?.addEventListener("abort", abortMonitor, { once: true });
|
|
13398
|
-
const delay = () => new Promise((
|
|
14099
|
+
const delay = () => new Promise((resolve9) => {
|
|
13399
14100
|
if (isAborted()) {
|
|
13400
|
-
|
|
14101
|
+
resolve9();
|
|
13401
14102
|
return;
|
|
13402
14103
|
}
|
|
13403
14104
|
const timer = setTimeout(finish, intervalMs);
|
|
13404
14105
|
function finish() {
|
|
13405
14106
|
clearTimeout(timer);
|
|
13406
14107
|
monitorController.signal.removeEventListener("abort", finish);
|
|
13407
|
-
|
|
14108
|
+
resolve9();
|
|
13408
14109
|
}
|
|
13409
14110
|
monitorController.signal.addEventListener("abort", finish, { once: true });
|
|
13410
14111
|
});
|
|
@@ -13516,6 +14217,7 @@ export {
|
|
|
13516
14217
|
HEARTBEAT_TIMEOUT_MS,
|
|
13517
14218
|
LAUNCH_SNAPSHOT_IDENTITY_VERSION,
|
|
13518
14219
|
MODEL_ALIAS_NAME,
|
|
14220
|
+
PLAIN_WORKFLOW_PROGRESS_STYLES,
|
|
13519
14221
|
PiRuntimeAgentRunner,
|
|
13520
14222
|
RPC_LIMIT_BYTES,
|
|
13521
14223
|
RUN_STATES,
|
|
@@ -13527,6 +14229,7 @@ export {
|
|
|
13527
14229
|
SessionLease,
|
|
13528
14230
|
THINKING_LEVELS,
|
|
13529
14231
|
TOOL_TIMING_ENTRY_TYPE,
|
|
14232
|
+
VIRTUAL_MODEL_PROVIDER,
|
|
13530
14233
|
WORKFLOW_AGENT_STALL_THRESHOLD_MS,
|
|
13531
14234
|
WORKFLOW_AGENT_STATE_CHANGED_EVENT,
|
|
13532
14235
|
WORKFLOW_BLOCKED_EVENT,
|
|
@@ -13569,9 +14272,11 @@ export {
|
|
|
13569
14272
|
buildWorkflowPhaseModel,
|
|
13570
14273
|
buildWorkflowPhaseTree,
|
|
13571
14274
|
byPriorityThenName,
|
|
14275
|
+
canonicalExtensionSelector,
|
|
13572
14276
|
canonicalPath,
|
|
13573
14277
|
coerceWorkflowError,
|
|
13574
14278
|
createHerdrAgentReporter,
|
|
14279
|
+
createInlineConfirm,
|
|
13575
14280
|
createLaunchSnapshot,
|
|
13576
14281
|
createLiveSessionHandoff,
|
|
13577
14282
|
createLocalPiSession,
|
|
@@ -13583,6 +14288,7 @@ export {
|
|
|
13583
14288
|
deepFreeze,
|
|
13584
14289
|
workflowExtension as default,
|
|
13585
14290
|
defineWorkflowFunction,
|
|
14291
|
+
discoverRoles,
|
|
13586
14292
|
encoded,
|
|
13587
14293
|
errorCode,
|
|
13588
14294
|
errorText,
|
|
@@ -13591,8 +14297,10 @@ export {
|
|
|
13591
14297
|
fail,
|
|
13592
14298
|
finiteNumber,
|
|
13593
14299
|
formatAgentDetail,
|
|
14300
|
+
formatAgentError,
|
|
13594
14301
|
formatBudgetStatus,
|
|
13595
14302
|
formatCost,
|
|
14303
|
+
formatNavigatorColumns,
|
|
13596
14304
|
formatNavigatorDashboard,
|
|
13597
14305
|
formatNavigatorRun,
|
|
13598
14306
|
formatStalledDuration,
|
|
@@ -13602,6 +14310,7 @@ export {
|
|
|
13602
14310
|
formatWorkflowPhaseDashboard,
|
|
13603
14311
|
formatWorkflowPreview,
|
|
13604
14312
|
formatWorkflowProgress,
|
|
14313
|
+
formatWorkflowRuntime,
|
|
13605
14314
|
hasLiveSessionLease,
|
|
13606
14315
|
herdrAvailable,
|
|
13607
14316
|
herdrCommandRunner,
|
|
@@ -13622,12 +14331,15 @@ export {
|
|
|
13622
14331
|
isTurnEnd,
|
|
13623
14332
|
isWorkflowAuthored,
|
|
13624
14333
|
isWorkflowErrorCode,
|
|
14334
|
+
jsonDigest,
|
|
13625
14335
|
jsonObject,
|
|
13626
14336
|
jsonValue,
|
|
13627
14337
|
listPersistedSessionIds,
|
|
13628
14338
|
loadAgentDefinitions,
|
|
14339
|
+
loadCodemodeToolsSetting,
|
|
13629
14340
|
loadLaunchSnapshot,
|
|
13630
14341
|
loadProjectAgentDefinitions,
|
|
14342
|
+
loadRole,
|
|
13631
14343
|
loadSettings,
|
|
13632
14344
|
loadSettingsOverrides,
|
|
13633
14345
|
loadingRegistry,
|
|
@@ -13654,13 +14366,16 @@ export {
|
|
|
13654
14366
|
parseThinking,
|
|
13655
14367
|
persistActiveAgentAttempt,
|
|
13656
14368
|
persistAgentAttempts,
|
|
14369
|
+
physicalModel,
|
|
13657
14370
|
positiveInteger,
|
|
13658
14371
|
preflight,
|
|
13659
14372
|
prepareAgentSetupForInspection,
|
|
13660
14373
|
preserveWorkflowPhaseSelection,
|
|
13661
14374
|
preserveWorkflowPhaseTreeSelection,
|
|
14375
|
+
progressStyleForState,
|
|
13662
14376
|
projectSessionsDirectory,
|
|
13663
14377
|
projectStorageKey,
|
|
14378
|
+
reachableTools,
|
|
13664
14379
|
readShellResult,
|
|
13665
14380
|
registerWorkflowExtension,
|
|
13666
14381
|
registeredWorkflowFunctionSources,
|
|
@@ -13671,6 +14386,7 @@ export {
|
|
|
13671
14386
|
resetWorkflowRegistryIfIdle,
|
|
13672
14387
|
resolveAgentResourcePolicy,
|
|
13673
14388
|
resolveModelReference,
|
|
14389
|
+
resolveRole,
|
|
13674
14390
|
resolveWorkflowSettings,
|
|
13675
14391
|
resourcePatternHasMagic,
|
|
13676
14392
|
resourcePatternMatches,
|
|
@@ -13678,17 +14394,20 @@ export {
|
|
|
13678
14394
|
retainTerminalRuns,
|
|
13679
14395
|
retainWorkflowRegistry,
|
|
13680
14396
|
roleNameOf,
|
|
14397
|
+
runStateGlyph,
|
|
13681
14398
|
runWorkflow,
|
|
13682
14399
|
runsDirectory,
|
|
13683
14400
|
runtimeProgressToAgentProgress,
|
|
13684
14401
|
sameFilesystemPath,
|
|
13685
14402
|
sanitizeDisplayText,
|
|
13686
14403
|
saveModelAliases,
|
|
14404
|
+
scriptToolReferences,
|
|
13687
14405
|
selectResourcesByLayers,
|
|
13688
14406
|
shellIdentityPath,
|
|
13689
14407
|
structuralPath,
|
|
13690
14408
|
sumAccounting,
|
|
13691
14409
|
themeWorkflowProgressStyles,
|
|
14410
|
+
toolIdentityPath,
|
|
13692
14411
|
truncateWorkflowProgress,
|
|
13693
14412
|
unknownModel,
|
|
13694
14413
|
unmatchedResourcePatterns,
|
|
@@ -13697,10 +14416,12 @@ export {
|
|
|
13697
14416
|
validateBudget,
|
|
13698
14417
|
validateBudgetPatch,
|
|
13699
14418
|
validateCheckpoint,
|
|
14419
|
+
validateContextFileScopes,
|
|
13700
14420
|
validateModelAliasAvailability,
|
|
13701
14421
|
validateModelAliases,
|
|
13702
14422
|
validateResourcePattern,
|
|
13703
14423
|
validateSchema,
|
|
14424
|
+
validateSelectorList,
|
|
13704
14425
|
validateShellCommand,
|
|
13705
14426
|
validateShellOptions,
|
|
13706
14427
|
validateWorkflowExtensionSettings,
|
|
@@ -13711,6 +14432,8 @@ export {
|
|
|
13711
14432
|
workflowCatalog,
|
|
13712
14433
|
workflowCatalogDetail,
|
|
13713
14434
|
workflowCatalogIndex,
|
|
14435
|
+
workflowKeyLabel,
|
|
14436
|
+
workflowKeyMatches,
|
|
13714
14437
|
workflowPhaseTreeInitialExpanded,
|
|
13715
14438
|
workflowPhaseTreeVisibleNodes,
|
|
13716
14439
|
workflowProjectSettingsPath,
|
|
@@ -13720,6 +14443,7 @@ export {
|
|
|
13720
14443
|
workflowRoleDirectories,
|
|
13721
14444
|
workflowScriptArtifact,
|
|
13722
14445
|
workflowSettingsPath,
|
|
14446
|
+
workflowToolExposure,
|
|
13723
14447
|
zeroAccounting
|
|
13724
14448
|
};
|
|
13725
14449
|
//# sourceMappingURL=index.js.map
|