@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/trajectory/index.js
CHANGED
|
@@ -5,7 +5,7 @@ import { mkdir as mkdir3, open as open3, readFile as readFile6, rm as rm5 } from
|
|
|
5
5
|
import { existsSync } from "node:fs";
|
|
6
6
|
import { dirname as dirname5, join as join8 } from "node:path";
|
|
7
7
|
import { fileURLToPath as fileURLToPath3 } from "node:url";
|
|
8
|
-
import { getAgentDir as
|
|
8
|
+
import { getAgentDir as getAgentDir4 } from "@earendil-works/pi-coding-agent";
|
|
9
9
|
|
|
10
10
|
// src/trajectory-host-handle.ts
|
|
11
11
|
var TRAJECTORY_HOST_KEY = /* @__PURE__ */ Symbol.for("pi-extensible-workflows.trajectory-host");
|
|
@@ -47,6 +47,7 @@ var ERROR_CODES = [
|
|
|
47
47
|
"AGENT_FAILED",
|
|
48
48
|
"AGENT_RESULT_COLLECTED",
|
|
49
49
|
"RESULT_INVALID",
|
|
50
|
+
"TOOL_FAILED",
|
|
50
51
|
"CANCELLED",
|
|
51
52
|
"WORKER_UNRESPONSIVE",
|
|
52
53
|
"WORKTREE_FAILED",
|
|
@@ -260,6 +261,12 @@ function resourcePatternHasMagic(pattern) {
|
|
|
260
261
|
function loadLaunchSnapshot(input) {
|
|
261
262
|
return deepFreeze(structuredClone(input));
|
|
262
263
|
}
|
|
264
|
+
function validateSchema(schema, at = "schema") {
|
|
265
|
+
if (!object(schema) || Object.getPrototypeOf(schema) !== Object.prototype || !jsonValue(schema)) fail("INVALID_SCHEMA", `${at} must be a plain JSON-compatible Schema object`);
|
|
266
|
+
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`);
|
|
267
|
+
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`);
|
|
268
|
+
if (schema.properties !== void 0 && !object(schema.properties)) fail("INVALID_SCHEMA", `${at}.properties must be an object`);
|
|
269
|
+
}
|
|
263
270
|
|
|
264
271
|
// src/decoders.ts
|
|
265
272
|
var INVALID_PERSISTED_VALUE = /* @__PURE__ */ Symbol("invalid persisted value");
|
|
@@ -394,7 +401,7 @@ function decodeWorkflowExtensions(value) {
|
|
|
394
401
|
}
|
|
395
402
|
if (namespace === "trajectory") {
|
|
396
403
|
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;
|
|
397
|
-
normalized.trajectory = { ...raw.port === void 0 ? {} : { port: raw.port }
|
|
404
|
+
normalized.trajectory = { ...raw.port === void 0 ? {} : { port: raw.port } };
|
|
398
405
|
continue;
|
|
399
406
|
}
|
|
400
407
|
if (!jsonValue(raw)) return void 0;
|
|
@@ -678,6 +685,22 @@ function decodeRunEvent(value) {
|
|
|
678
685
|
if (timestamp === INVALID_PERSISTED_VALUE) return void 0;
|
|
679
686
|
return { type: value.type, message: value.message, ...timestamp === void 0 ? {} : { timestamp } };
|
|
680
687
|
}
|
|
688
|
+
function decodeScriptCall(value) {
|
|
689
|
+
if (!object(value) || value.kind !== "shell" && value.kind !== "tool" || typeof value.name !== "string" || typeof value.path !== "string" || !finiteNumber(value.startedAt)) return void 0;
|
|
690
|
+
const shell = value.kind === "shell";
|
|
691
|
+
if (shell && !jsonValue(value.input)) return void 0;
|
|
692
|
+
const inputBytes = shell ? optionalNumber(value.inputBytes) : void 0;
|
|
693
|
+
const phase = optionalString(value.phase);
|
|
694
|
+
const finishedAt = optionalNumber(value.finishedAt);
|
|
695
|
+
let error;
|
|
696
|
+
if (value.error !== void 0) {
|
|
697
|
+
if (shell) error = decodeWorkflowError(value.error);
|
|
698
|
+
else if (object(value.error) && typeof value.error.code === "string") error = { code: isWorkflowErrorCode(value.error.code) ? value.error.code : "INTERNAL_ERROR" };
|
|
699
|
+
if (!error) return void 0;
|
|
700
|
+
}
|
|
701
|
+
if (inputBytes === INVALID_PERSISTED_VALUE || phase === INVALID_PERSISTED_VALUE || finishedAt === INVALID_PERSISTED_VALUE) return void 0;
|
|
702
|
+
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 } };
|
|
703
|
+
}
|
|
681
704
|
function decodeDelivery(value) {
|
|
682
705
|
if (!object(value)) return void 0;
|
|
683
706
|
const mode = value.mode;
|
|
@@ -707,6 +730,8 @@ function decodePersistedRun(value, allowLegacyAgentSessions = false) {
|
|
|
707
730
|
const budgetEvents = value.budgetEvents === void 0 ? void 0 : decodeArray(value.budgetEvents, decodeBudgetEvent);
|
|
708
731
|
const events = value.events === void 0 ? void 0 : decodeArray(value.events, decodeRunEvent);
|
|
709
732
|
const delivery = value.delivery === void 0 ? void 0 : decodeDelivery(value.delivery);
|
|
733
|
+
const scriptCalls = value.scriptCalls === void 0 ? void 0 : decodeArray(value.scriptCalls, decodeScriptCall);
|
|
734
|
+
if (value.scriptCalls !== void 0 && !scriptCalls) return void 0;
|
|
710
735
|
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;
|
|
711
736
|
if (value.agentSessions === void 0 && !allowLegacyAgentSessions) return void 0;
|
|
712
737
|
return {
|
|
@@ -725,6 +750,7 @@ function decodePersistedRun(value, allowLegacyAgentSessions = false) {
|
|
|
725
750
|
...activeShells === void 0 ? {} : { activeShells },
|
|
726
751
|
...activeShellStartedAt === void 0 ? {} : { activeShellStartedAt },
|
|
727
752
|
...activeShellsByPhase === void 0 ? {} : { activeShellsByPhase },
|
|
753
|
+
...scriptCalls === void 0 ? {} : { scriptCalls },
|
|
728
754
|
...error === void 0 ? {} : { error },
|
|
729
755
|
...failedAt === void 0 ? {} : { failedAt },
|
|
730
756
|
...budget === void 0 ? {} : { budget },
|
|
@@ -1012,8 +1038,8 @@ async function listRunIds(cwd, sessionId, home = homedir2(), cleanTemps = true)
|
|
|
1012
1038
|
// src/trajectory.ts
|
|
1013
1039
|
import { open as open2, readFile as readFile4, readdir as readdir3, stat as stat3 } from "node:fs/promises";
|
|
1014
1040
|
import { homedir as homedir5 } from "node:os";
|
|
1015
|
-
import { basename as
|
|
1016
|
-
import { createBashToolDefinition, createEditToolDefinition, createFindToolDefinition, createGrepToolDefinition, createLsToolDefinition, createReadToolDefinition, createWriteToolDefinition, DefaultPackageManager, getAgentDir as
|
|
1041
|
+
import { basename as basename3, dirname as dirname4, join as join6, resolve as resolve4 } from "node:path";
|
|
1042
|
+
import { createBashToolDefinition, createEditToolDefinition, createFindToolDefinition, createGrepToolDefinition, createLsToolDefinition, createReadToolDefinition, createWriteToolDefinition, DefaultPackageManager, getAgentDir as getAgentDir3, SettingsManager, DEFAULT_MAX_BYTES } from "@earendil-works/pi-coding-agent";
|
|
1017
1043
|
|
|
1018
1044
|
// src/store.ts
|
|
1019
1045
|
import { execFile as execFile2 } from "node:child_process";
|
|
@@ -1878,7 +1904,7 @@ var RunStore = class _RunStore {
|
|
|
1878
1904
|
|
|
1879
1905
|
// src/host-view.ts
|
|
1880
1906
|
import { keyHint, truncateToVisualLines } from "@earendil-works/pi-coding-agent";
|
|
1881
|
-
import { truncateToWidth as truncateToWidth2 } from "@earendil-works/pi-tui";
|
|
1907
|
+
import { truncateToWidth as truncateToWidth2, visibleWidth as visibleWidth2 } from "@earendil-works/pi-tui";
|
|
1882
1908
|
|
|
1883
1909
|
// src/background-widget.ts
|
|
1884
1910
|
import { isKeyRelease, Key, matchesKey, truncateToWidth, visibleWidth } from "@earendil-works/pi-tui";
|
|
@@ -1892,19 +1918,211 @@ var ANSI_SGR = new RegExp(ANSI_SGR_SOURCE);
|
|
|
1892
1918
|
import { Type } from "@earendil-works/pi-ai";
|
|
1893
1919
|
|
|
1894
1920
|
// src/validation.ts
|
|
1895
|
-
import { mkdirSync, readFileSync as readFileSync2, readdirSync, statSync } from "node:fs";
|
|
1896
|
-
import { homedir as homedir4 } from "node:os";
|
|
1897
|
-
import { basename as basename3, dirname as dirname3, extname, join as join5, resolve as resolve3 } from "node:path";
|
|
1898
|
-
import { fileURLToPath as fileURLToPath2 } from "node:url";
|
|
1899
1921
|
import * as acorn from "acorn";
|
|
1900
|
-
|
|
1922
|
+
|
|
1923
|
+
// src/roles.ts
|
|
1924
|
+
import { getAgentDir as getAgentDir2, parseFrontmatter } from "@earendil-works/pi-coding-agent";
|
|
1901
1925
|
|
|
1902
1926
|
// src/registry.ts
|
|
1903
|
-
import { readFileSync } from "node:fs";
|
|
1904
|
-
import { basename as basename2, dirname as
|
|
1905
|
-
import { fileURLToPath } from "node:url";
|
|
1927
|
+
import { readFileSync as readFileSync2 } from "node:fs";
|
|
1928
|
+
import { basename as basename2, dirname as dirname3, isAbsolute, join as join5 } from "node:path";
|
|
1929
|
+
import { fileURLToPath as fileURLToPath2 } from "node:url";
|
|
1906
1930
|
import { Value } from "typebox/value";
|
|
1907
1931
|
|
|
1932
|
+
// src/settings.ts
|
|
1933
|
+
import { mkdirSync, readFileSync } from "node:fs";
|
|
1934
|
+
import { homedir as homedir4 } from "node:os";
|
|
1935
|
+
import { dirname as dirname2, join as join4, resolve as resolve3 } from "node:path";
|
|
1936
|
+
import { fileURLToPath } from "node:url";
|
|
1937
|
+
import { getAgentDir } from "@earendil-works/pi-coding-agent";
|
|
1938
|
+
var ROLE_DIRECTORY = "pi-extensible-workflows";
|
|
1939
|
+
var DEFAULT_SETTINGS = Object.freeze({ concurrency: 8, backgroundWidget: true });
|
|
1940
|
+
function workflowSettingsPath(agentDir = getAgentDir()) {
|
|
1941
|
+
return join4(agentDir, ROLE_DIRECTORY, "settings.json");
|
|
1942
|
+
}
|
|
1943
|
+
function normalizedResourcePath(value, settingsPath) {
|
|
1944
|
+
if (value === "*" || value.startsWith("builtin:")) return value;
|
|
1945
|
+
let expanded = value === "~" ? homedir4() : value.startsWith("~/") || value.startsWith("~\\") ? join4(homedir4(), value.slice(2)) : value;
|
|
1946
|
+
if (expanded.startsWith("file://")) expanded = fileURLToPath(expanded);
|
|
1947
|
+
const resolved = resolve3(dirname2(settingsPath), expanded);
|
|
1948
|
+
if (expanded === "**" || expanded.startsWith("**/") || expanded.startsWith("**\\")) return expanded;
|
|
1949
|
+
if (resourcePatternHasMagic(expanded)) {
|
|
1950
|
+
const magicIndex = resolved.search(/[*?\x5b\x5d{}()]/);
|
|
1951
|
+
const separatorIndex = Math.max(resolved.lastIndexOf("/", magicIndex), resolved.lastIndexOf("\\", magicIndex));
|
|
1952
|
+
const rootBoundary = separatorIndex === 0 || separatorIndex === 2 && /^[A-Za-z]:[\\/]/.test(resolved);
|
|
1953
|
+
const prefix = rootBoundary ? resolved.slice(0, separatorIndex + 1) : separatorIndex >= 0 ? resolved.slice(0, separatorIndex) : resolved;
|
|
1954
|
+
const suffix = rootBoundary ? resolved.slice(separatorIndex + 1) : separatorIndex >= 0 ? resolved.slice(separatorIndex) : "";
|
|
1955
|
+
return `${canonicalPath(prefix)}${suffix}`;
|
|
1956
|
+
}
|
|
1957
|
+
return canonicalPath(resolved);
|
|
1958
|
+
}
|
|
1959
|
+
function validateSelectorList(value, path, kind, errorCode2 = "INVALID_SETTINGS", normalizeExtensions = true) {
|
|
1960
|
+
if (value === void 0) return void 0;
|
|
1961
|
+
if (!Array.isArray(value)) fail(errorCode2, `${path}.${kind} must be an array`);
|
|
1962
|
+
const normalized = [];
|
|
1963
|
+
for (const [index, entry] of value.entries()) {
|
|
1964
|
+
if (typeof entry !== "string" || !entry.trim()) fail(errorCode2, `${path}.${kind}[${String(index)}] must be a non-empty string`);
|
|
1965
|
+
let selector = entry.trim();
|
|
1966
|
+
if (kind === "extensions" && normalizeExtensions) {
|
|
1967
|
+
const negated = selector.startsWith("!");
|
|
1968
|
+
const body = negated ? selector.slice(1) : selector;
|
|
1969
|
+
if (!body) fail(errorCode2, `${path}.${kind}[${String(index)}] must be a valid minimatch pattern: Empty minimatch pattern ${JSON.stringify(selector)}`);
|
|
1970
|
+
try {
|
|
1971
|
+
selector = `${negated ? "!" : ""}${normalizedResourcePath(body, path)}`;
|
|
1972
|
+
} catch (error) {
|
|
1973
|
+
fail(errorCode2, `${path}.${kind}[${String(index)}] must be a valid path: ${errorText(error)}`);
|
|
1974
|
+
}
|
|
1975
|
+
}
|
|
1976
|
+
try {
|
|
1977
|
+
validateResourcePattern(selector);
|
|
1978
|
+
} catch (error) {
|
|
1979
|
+
fail(errorCode2, `${path}.${kind}[${String(index)}] must be a valid minimatch pattern: ${errorText(error)}`);
|
|
1980
|
+
}
|
|
1981
|
+
normalized.push(selector);
|
|
1982
|
+
}
|
|
1983
|
+
return Object.freeze(normalized);
|
|
1984
|
+
}
|
|
1985
|
+
function validateWorktreePostCreateCommand(value, settingsPath) {
|
|
1986
|
+
if (value === void 0) return void 0;
|
|
1987
|
+
if (!Array.isArray(value) || value.length === 0) fail("INVALID_SETTINGS", `${settingsPath}.worktreePostCreateCommand must be a non-empty array`);
|
|
1988
|
+
return Object.freeze(value.map((entry, index) => {
|
|
1989
|
+
if (typeof entry !== "string" || !entry.trim()) fail("INVALID_SETTINGS", `${settingsPath}.worktreePostCreateCommand[${String(index)}] must be a non-empty string`);
|
|
1990
|
+
return entry;
|
|
1991
|
+
}));
|
|
1992
|
+
}
|
|
1993
|
+
function selectorsFromSettings(settings) {
|
|
1994
|
+
return {
|
|
1995
|
+
...settings.skills === void 0 ? {} : { skills: settings.skills },
|
|
1996
|
+
...settings.extensions === void 0 ? {} : { extensions: settings.extensions },
|
|
1997
|
+
...settings.tools === void 0 ? {} : { tools: settings.tools }
|
|
1998
|
+
};
|
|
1999
|
+
}
|
|
2000
|
+
function selectorSet(value) {
|
|
2001
|
+
return { skills: [...value?.skills ?? []], extensions: [...value?.extensions ?? []], ...value?.tools === void 0 ? {} : { tools: [...value.tools] } };
|
|
2002
|
+
}
|
|
2003
|
+
function validateWorkflowExtensionSettings(value, settingsPath, errorCode2 = "INVALID_SETTINGS") {
|
|
2004
|
+
if (value === void 0) return void 0;
|
|
2005
|
+
const base = `${settingsPath}.extensionSettings`;
|
|
2006
|
+
if (!object(value)) fail(errorCode2, `${base} must be an object`);
|
|
2007
|
+
const normalized = {};
|
|
2008
|
+
for (const [namespace, raw] of Object.entries(value)) {
|
|
2009
|
+
if (!validWorkflowExtensionNamespace(namespace)) fail(errorCode2, `${base} contains an invalid namespace: ${namespace}`);
|
|
2010
|
+
if (namespace === "herdr") {
|
|
2011
|
+
if (!object(raw)) fail(errorCode2, `${base}.herdr must be an object`);
|
|
2012
|
+
if (Object.keys(raw).some((key) => key !== "enableFullyInspectableMode")) fail(errorCode2, `${base}.herdr contains an unsupported setting`);
|
|
2013
|
+
if (raw.enableFullyInspectableMode !== void 0 && typeof raw.enableFullyInspectableMode !== "boolean") fail(errorCode2, `${base}.herdr.enableFullyInspectableMode must be a boolean`);
|
|
2014
|
+
normalized.herdr = Object.freeze({ ...raw.enableFullyInspectableMode === void 0 ? {} : { enableFullyInspectableMode: raw.enableFullyInspectableMode } });
|
|
2015
|
+
continue;
|
|
2016
|
+
}
|
|
2017
|
+
if (namespace === "trajectory") {
|
|
2018
|
+
if (!object(raw)) fail(errorCode2, `${base}.trajectory must be an object`);
|
|
2019
|
+
if (Object.keys(raw).some((key) => key !== "port")) fail(errorCode2, `${base}.trajectory contains an unsupported setting`);
|
|
2020
|
+
if (raw.port !== void 0 && (!positiveInteger(raw.port) || raw.port > 65535)) fail(errorCode2, `${base}.trajectory.port must be an integer from 1 to 65535`);
|
|
2021
|
+
normalized.trajectory = Object.freeze({ ...raw.port === void 0 ? {} : { port: raw.port } });
|
|
2022
|
+
continue;
|
|
2023
|
+
}
|
|
2024
|
+
if (!jsonValue(raw)) fail(errorCode2, `${base}.${namespace} must be JSON-compatible`);
|
|
2025
|
+
normalized[namespace] = structuredClone(raw);
|
|
2026
|
+
}
|
|
2027
|
+
return deepFreeze(normalized);
|
|
2028
|
+
}
|
|
2029
|
+
function positiveRetentionInteger(value) {
|
|
2030
|
+
return positiveInteger(value) && Number.isSafeInteger(value);
|
|
2031
|
+
}
|
|
2032
|
+
function validateRetention(value, settingsPath) {
|
|
2033
|
+
if (value === void 0) return void 0;
|
|
2034
|
+
const base = `${settingsPath}.retention`;
|
|
2035
|
+
if (!object(value)) fail("INVALID_SETTINGS", `${base} must be an object`);
|
|
2036
|
+
const unknown = Object.keys(value).find((key) => key !== "olderThanDays" && key !== "maxTerminalRuns");
|
|
2037
|
+
if (unknown) fail("INVALID_SETTINGS", `Unknown retention setting at ${base}: ${unknown}`);
|
|
2038
|
+
if (value.olderThanDays !== void 0 && !positiveRetentionInteger(value.olderThanDays)) fail("INVALID_SETTINGS", `${base}.olderThanDays must be a positive integer`);
|
|
2039
|
+
if (value.maxTerminalRuns !== void 0 && !positiveRetentionInteger(value.maxTerminalRuns)) fail("INVALID_SETTINGS", `${base}.maxTerminalRuns must be a positive integer`);
|
|
2040
|
+
return Object.freeze({ ...value.olderThanDays === void 0 ? {} : { olderThanDays: value.olderThanDays }, ...value.maxTerminalRuns === void 0 ? {} : { maxTerminalRuns: value.maxTerminalRuns } });
|
|
2041
|
+
}
|
|
2042
|
+
function parseSettings(path, partial) {
|
|
2043
|
+
let parsed;
|
|
2044
|
+
try {
|
|
2045
|
+
parsed = JSON.parse(readFileSync(path, "utf8"));
|
|
2046
|
+
} catch (error) {
|
|
2047
|
+
if (isNodeError(error, "ENOENT")) return partial ? Object.freeze({}) : DEFAULT_SETTINGS;
|
|
2048
|
+
fail("CONFIG_ERROR", `Invalid workflow settings JSON at ${path}: ${errorText(error)}`);
|
|
2049
|
+
}
|
|
2050
|
+
if (!object(parsed)) fail("INVALID_SETTINGS", `Workflow settings at ${path} must be an object`);
|
|
2051
|
+
const allowed = /* @__PURE__ */ new Set(["concurrency", "modelAliases", "skills", "extensions", "extensionSettings", "tools", "retention", "worktreePostCreateCommand", ...partial ? [] : ["backgroundWidget", "codemodeTools"]]);
|
|
2052
|
+
const unknown = Object.keys(parsed).find((key) => !allowed.has(key));
|
|
2053
|
+
if (Object.prototype.hasOwnProperty.call(parsed, "disabledAgentResources")) fail("INVALID_SETTINGS", `disabledAgentResources is no longer supported; use skills, extensions, and tools selectors (settings: ${path})`);
|
|
2054
|
+
if (unknown) fail("INVALID_SETTINGS", `Unknown workflow setting at ${path}: ${unknown}`);
|
|
2055
|
+
const concurrency = parsed.concurrency === void 0 ? partial ? void 0 : DEFAULT_SETTINGS.concurrency : parsed.concurrency;
|
|
2056
|
+
if (concurrency !== void 0 && (!positiveInteger(concurrency) || concurrency > 16)) fail("INVALID_SETTINGS", `${path}.concurrency must be an integer from 1 to 16`);
|
|
2057
|
+
const backgroundWidget = parsed.backgroundWidget === void 0 ? partial ? void 0 : DEFAULT_SETTINGS.backgroundWidget : parsed.backgroundWidget;
|
|
2058
|
+
if (backgroundWidget !== void 0 && typeof backgroundWidget !== "boolean") fail("INVALID_SETTINGS", `${path}.backgroundWidget must be a boolean`);
|
|
2059
|
+
const codemodeTools = parsed.codemodeTools;
|
|
2060
|
+
if (codemodeTools !== void 0 && codemodeTools !== "all" && codemodeTools !== "read-only" && codemodeTools !== "none") fail("INVALID_SETTINGS", `${path}.codemodeTools must be "all", "read-only", or "none"`);
|
|
2061
|
+
const modelAliases = parsed.modelAliases === void 0 ? void 0 : validateModelAliases(parsed.modelAliases, path);
|
|
2062
|
+
const skills = validateSelectorList(parsed.skills, path, "skills");
|
|
2063
|
+
const tools = validateSelectorList(parsed.tools, path, "tools");
|
|
2064
|
+
const extensions = validateSelectorList(parsed.extensions, path, "extensions");
|
|
2065
|
+
const extensionSettings = parsed.extensionSettings === void 0 ? void 0 : validateWorkflowExtensionSettings(parsed.extensionSettings, path, "INVALID_SETTINGS");
|
|
2066
|
+
const retention = validateRetention(parsed.retention, path);
|
|
2067
|
+
const worktreePostCreateCommand = validateWorktreePostCreateCommand(parsed.worktreePostCreateCommand, path);
|
|
2068
|
+
return Object.freeze({
|
|
2069
|
+
...concurrency === void 0 ? {} : { concurrency },
|
|
2070
|
+
...backgroundWidget === void 0 ? {} : { backgroundWidget },
|
|
2071
|
+
...codemodeTools === void 0 ? {} : { codemodeTools },
|
|
2072
|
+
...modelAliases === void 0 ? {} : { modelAliases },
|
|
2073
|
+
...skills === void 0 ? {} : { skills },
|
|
2074
|
+
...extensions === void 0 ? {} : { extensions },
|
|
2075
|
+
...extensionSettings === void 0 ? {} : { extensionSettings },
|
|
2076
|
+
...tools === void 0 ? {} : { tools },
|
|
2077
|
+
...retention === void 0 ? {} : { retention },
|
|
2078
|
+
...worktreePostCreateCommand === void 0 ? {} : { worktreePostCreateCommand }
|
|
2079
|
+
});
|
|
2080
|
+
}
|
|
2081
|
+
function loadSettings(path = workflowSettingsPath()) {
|
|
2082
|
+
return parseSettings(path, false);
|
|
2083
|
+
}
|
|
2084
|
+
function loadSettingsOverrides(path) {
|
|
2085
|
+
return parseSettings(path, true);
|
|
2086
|
+
}
|
|
2087
|
+
function resolveWorkflowSettings(cwd, projectTrusted, globalSettingsPath = workflowSettingsPath()) {
|
|
2088
|
+
const projectSettingsPath = workflowProjectSettingsPath(cwd);
|
|
2089
|
+
const global = loadSettings(globalSettingsPath);
|
|
2090
|
+
const project = projectTrusted ? loadSettingsOverrides(projectSettingsPath) : Object.freeze({});
|
|
2091
|
+
const projectHas = (key) => Object.prototype.hasOwnProperty.call(project, key);
|
|
2092
|
+
const sourceFor = (key) => projectHas(key) ? projectSettingsPath : globalSettingsPath;
|
|
2093
|
+
const globalSelectors = selectorsFromSettings(global);
|
|
2094
|
+
const projectSelectors = selectorsFromSettings(project);
|
|
2095
|
+
const effectiveSelectors = selectorSet({
|
|
2096
|
+
skills: [...globalSelectors.skills ?? [], ...projectSelectors.skills ?? []],
|
|
2097
|
+
extensions: [...globalSelectors.extensions ?? [], ...projectSelectors.extensions ?? []],
|
|
2098
|
+
...globalSelectors.tools === void 0 && projectSelectors.tools === void 0 ? {} : { tools: [...globalSelectors.tools ?? [], ...projectSelectors.tools ?? []] }
|
|
2099
|
+
});
|
|
2100
|
+
const hasExtensionSelectors = global.extensions !== void 0 || project.extensions !== void 0;
|
|
2101
|
+
const extensionSettings = projectHas("extensionSettings") ? project.extensionSettings : global.extensionSettings;
|
|
2102
|
+
const sources = {
|
|
2103
|
+
concurrency: projectHas("concurrency") ? projectSettingsPath : globalSettingsPath,
|
|
2104
|
+
modelAliases: projectHas("modelAliases") ? projectSettingsPath : globalSettingsPath,
|
|
2105
|
+
skills: sourceFor("skills"),
|
|
2106
|
+
extensions: sourceFor("extensions"),
|
|
2107
|
+
tools: sourceFor("tools"),
|
|
2108
|
+
...extensionSettings === void 0 ? {} : { extensionSettings: projectHas("extensionSettings") ? projectSettingsPath : globalSettingsPath },
|
|
2109
|
+
...project.retention === void 0 && global.retention === void 0 ? {} : { retention: project.retention === void 0 ? globalSettingsPath : projectSettingsPath },
|
|
2110
|
+
...projectHas("worktreePostCreateCommand") || global.worktreePostCreateCommand !== void 0 ? { worktreePostCreateCommand: projectHas("worktreePostCreateCommand") ? projectSettingsPath : globalSettingsPath } : {}
|
|
2111
|
+
};
|
|
2112
|
+
const effective = Object.freeze({
|
|
2113
|
+
concurrency: project.concurrency ?? global.concurrency,
|
|
2114
|
+
backgroundWidget: global.backgroundWidget ?? true,
|
|
2115
|
+
...projectHas("modelAliases") ? { modelAliases: project.modelAliases } : global.modelAliases === void 0 ? {} : { modelAliases: global.modelAliases },
|
|
2116
|
+
...effectiveSelectors.skills.length ? { skills: effectiveSelectors.skills } : global.skills !== void 0 || project.skills !== void 0 ? { skills: effectiveSelectors.skills } : {},
|
|
2117
|
+
...hasExtensionSelectors ? { extensions: effectiveSelectors.extensions } : {},
|
|
2118
|
+
...extensionSettings === void 0 ? {} : { extensionSettings },
|
|
2119
|
+
...effectiveSelectors.tools?.length ? { tools: effectiveSelectors.tools } : global.tools !== void 0 || project.tools !== void 0 ? { tools: effectiveSelectors.tools } : {},
|
|
2120
|
+
...(project.retention ?? global.retention) === void 0 ? {} : { retention: project.retention ?? global.retention },
|
|
2121
|
+
...projectHas("worktreePostCreateCommand") ? { worktreePostCreateCommand: project.worktreePostCreateCommand } : global.worktreePostCreateCommand === void 0 ? {} : { worktreePostCreateCommand: global.worktreePostCreateCommand }
|
|
2122
|
+
});
|
|
2123
|
+
return { globalSettingsPath, projectSettingsPath, projectTrusted, global, project, effective, sources };
|
|
2124
|
+
}
|
|
2125
|
+
|
|
1908
2126
|
// src/package-identity.ts
|
|
1909
2127
|
var CORE_PACKAGE_NAME = "@marcoscale98/pi-extensible-workflows";
|
|
1910
2128
|
var CORE_PACKAGE_COMPATIBILITY_NAME = "pi-extensible-workflows";
|
|
@@ -1914,7 +2132,7 @@ function isCorePackageName(value) {
|
|
|
1914
2132
|
}
|
|
1915
2133
|
|
|
1916
2134
|
// src/registry.ts
|
|
1917
|
-
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"]);
|
|
2135
|
+
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"]);
|
|
1918
2136
|
var IDENTIFIER = /^[A-Za-z_$][\w$]*$/;
|
|
1919
2137
|
var SEMVER = /^\d+\.\d+\.\d+(?:-[0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*)?(?:\+[0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*)?$/;
|
|
1920
2138
|
var PACKAGE_NAME = /^(?:@[a-z0-9][a-z0-9._-]*\/)?[a-z0-9][a-z0-9._-]*$/;
|
|
@@ -1922,10 +2140,10 @@ function normalizeRoleDirectory(value) {
|
|
|
1922
2140
|
try {
|
|
1923
2141
|
if (value instanceof URL) {
|
|
1924
2142
|
if (value.protocol !== "file:") fail("INVALID_METADATA", "Workflow role directories require file URLs or absolute filesystem paths");
|
|
1925
|
-
return
|
|
2143
|
+
return fileURLToPath2(value);
|
|
1926
2144
|
}
|
|
1927
2145
|
if (typeof value === "string") {
|
|
1928
|
-
if (value.startsWith("file:")) return
|
|
2146
|
+
if (value.startsWith("file:")) return fileURLToPath2(value);
|
|
1929
2147
|
if (isAbsolute(value)) return value;
|
|
1930
2148
|
}
|
|
1931
2149
|
} catch (error) {
|
|
@@ -1935,12 +2153,12 @@ function normalizeRoleDirectory(value) {
|
|
|
1935
2153
|
}
|
|
1936
2154
|
function isBuiltinRoleDirectory(path) {
|
|
1937
2155
|
const canonical = canonicalPath(path);
|
|
1938
|
-
const starterDirectory =
|
|
2156
|
+
const starterDirectory = dirname3(canonical);
|
|
1939
2157
|
if (basename2(canonical) !== "roles" || basename2(starterDirectory) !== "starter") return false;
|
|
1940
|
-
const distributionDirectory =
|
|
1941
|
-
const packageRoot = basename2(distributionDirectory) === "dist" ?
|
|
2158
|
+
const distributionDirectory = dirname3(starterDirectory);
|
|
2159
|
+
const packageRoot = basename2(distributionDirectory) === "dist" ? dirname3(distributionDirectory) : distributionDirectory;
|
|
1942
2160
|
try {
|
|
1943
|
-
const metadata = JSON.parse(
|
|
2161
|
+
const metadata = JSON.parse(readFileSync2(join5(packageRoot, "package.json"), "utf8"));
|
|
1944
2162
|
return object(metadata) && isCorePackageName(metadata.name);
|
|
1945
2163
|
} catch {
|
|
1946
2164
|
return false;
|
|
@@ -2214,199 +2432,6 @@ function beginWorkflowExtensionLoading() {
|
|
|
2214
2432
|
}
|
|
2215
2433
|
beginWorkflowExtensionLoading();
|
|
2216
2434
|
|
|
2217
|
-
// src/validation.ts
|
|
2218
|
-
var DEFAULT_SETTINGS = Object.freeze({ concurrency: 8, backgroundWidget: true });
|
|
2219
|
-
function workflowSettingsPath(agentDir = getAgentDir()) {
|
|
2220
|
-
return join5(agentDir, ROLE_DIRECTORY, "settings.json");
|
|
2221
|
-
}
|
|
2222
|
-
function normalizedResourcePath(value, settingsPath) {
|
|
2223
|
-
if (value === "*") return value;
|
|
2224
|
-
let expanded = value === "~" ? homedir4() : value.startsWith("~/") || value.startsWith("~\\") ? join5(homedir4(), value.slice(2)) : value;
|
|
2225
|
-
if (expanded.startsWith("file://")) expanded = fileURLToPath2(expanded);
|
|
2226
|
-
const resolved = resolve3(dirname3(settingsPath), expanded);
|
|
2227
|
-
if (expanded === "**" || expanded.startsWith("**/") || expanded.startsWith("**\\")) return expanded;
|
|
2228
|
-
if (resourcePatternHasMagic(expanded)) {
|
|
2229
|
-
const magicIndex = resolved.search(/[*?\x5b\x5d{}()]/);
|
|
2230
|
-
const separatorIndex = Math.max(resolved.lastIndexOf("/", magicIndex), resolved.lastIndexOf("\\", magicIndex));
|
|
2231
|
-
const rootBoundary = separatorIndex === 0 || separatorIndex === 2 && /^[A-Za-z]:[\\/]/.test(resolved);
|
|
2232
|
-
const prefix = rootBoundary ? resolved.slice(0, separatorIndex + 1) : separatorIndex >= 0 ? resolved.slice(0, separatorIndex) : resolved;
|
|
2233
|
-
const suffix = rootBoundary ? resolved.slice(separatorIndex + 1) : separatorIndex >= 0 ? resolved.slice(separatorIndex) : "";
|
|
2234
|
-
return `${canonicalPath(prefix)}${suffix}`;
|
|
2235
|
-
}
|
|
2236
|
-
return canonicalPath(resolved);
|
|
2237
|
-
}
|
|
2238
|
-
function validateSelectorList(value, path, kind, errorCode2 = "INVALID_SETTINGS", normalizeExtensions = true) {
|
|
2239
|
-
if (value === void 0) return void 0;
|
|
2240
|
-
if (!Array.isArray(value)) fail(errorCode2, `${path}.${kind} must be an array`);
|
|
2241
|
-
const normalized = [];
|
|
2242
|
-
for (const [index, entry] of value.entries()) {
|
|
2243
|
-
if (typeof entry !== "string" || !entry.trim()) fail(errorCode2, `${path}.${kind}[${String(index)}] must be a non-empty string`);
|
|
2244
|
-
let selector = entry.trim();
|
|
2245
|
-
if (kind === "extensions" && normalizeExtensions) {
|
|
2246
|
-
const negated = selector.startsWith("!");
|
|
2247
|
-
const body = negated ? selector.slice(1) : selector;
|
|
2248
|
-
if (!body) fail(errorCode2, `${path}.${kind}[${String(index)}] must be a valid minimatch pattern: Empty minimatch pattern ${JSON.stringify(selector)}`);
|
|
2249
|
-
try {
|
|
2250
|
-
selector = `${negated ? "!" : ""}${normalizedResourcePath(body, path)}`;
|
|
2251
|
-
} catch (error) {
|
|
2252
|
-
fail(errorCode2, `${path}.${kind}[${String(index)}] must be a valid path: ${errorText(error)}`);
|
|
2253
|
-
}
|
|
2254
|
-
}
|
|
2255
|
-
try {
|
|
2256
|
-
validateResourcePattern(selector);
|
|
2257
|
-
} catch (error) {
|
|
2258
|
-
fail(errorCode2, `${path}.${kind}[${String(index)}] must be a valid minimatch pattern: ${errorText(error)}`);
|
|
2259
|
-
}
|
|
2260
|
-
normalized.push(selector);
|
|
2261
|
-
}
|
|
2262
|
-
return Object.freeze(normalized);
|
|
2263
|
-
}
|
|
2264
|
-
function validateWorktreePostCreateCommand(value, settingsPath) {
|
|
2265
|
-
if (value === void 0) return void 0;
|
|
2266
|
-
if (!Array.isArray(value) || value.length === 0) fail("INVALID_SETTINGS", `${settingsPath}.worktreePostCreateCommand must be a non-empty array`);
|
|
2267
|
-
return Object.freeze(value.map((entry, index) => {
|
|
2268
|
-
if (typeof entry !== "string" || !entry.trim()) fail("INVALID_SETTINGS", `${settingsPath}.worktreePostCreateCommand[${String(index)}] must be a non-empty string`);
|
|
2269
|
-
return entry;
|
|
2270
|
-
}));
|
|
2271
|
-
}
|
|
2272
|
-
function selectorsFromSettings(settings) {
|
|
2273
|
-
return {
|
|
2274
|
-
...settings.skills === void 0 ? {} : { skills: settings.skills },
|
|
2275
|
-
...settings.extensions === void 0 ? {} : { extensions: settings.extensions },
|
|
2276
|
-
...settings.tools === void 0 ? {} : { tools: settings.tools }
|
|
2277
|
-
};
|
|
2278
|
-
}
|
|
2279
|
-
function selectorSet(value) {
|
|
2280
|
-
return { skills: [...value?.skills ?? []], extensions: [...value?.extensions ?? []], ...value?.tools === void 0 ? {} : { tools: [...value.tools] } };
|
|
2281
|
-
}
|
|
2282
|
-
function validateWorkflowExtensionSettings(value, settingsPath, errorCode2 = "INVALID_SETTINGS", applyDefaults = true) {
|
|
2283
|
-
if (value === void 0) return void 0;
|
|
2284
|
-
const base = `${settingsPath}.extensionSettings`;
|
|
2285
|
-
if (!object(value)) fail(errorCode2, `${base} must be an object`);
|
|
2286
|
-
const normalized = {};
|
|
2287
|
-
for (const [namespace, raw] of Object.entries(value)) {
|
|
2288
|
-
if (!validWorkflowExtensionNamespace(namespace)) fail(errorCode2, `${base} contains an invalid namespace: ${namespace}`);
|
|
2289
|
-
if (namespace === "herdr") {
|
|
2290
|
-
if (!object(raw)) fail(errorCode2, `${base}.herdr must be an object`);
|
|
2291
|
-
if (Object.keys(raw).some((key) => key !== "enableFullyInspectableMode")) fail(errorCode2, `${base}.herdr contains an unsupported setting`);
|
|
2292
|
-
if (raw.enableFullyInspectableMode !== void 0 && typeof raw.enableFullyInspectableMode !== "boolean") fail(errorCode2, `${base}.herdr.enableFullyInspectableMode must be a boolean`);
|
|
2293
|
-
normalized.herdr = Object.freeze({ ...raw.enableFullyInspectableMode === void 0 ? {} : { enableFullyInspectableMode: raw.enableFullyInspectableMode } });
|
|
2294
|
-
continue;
|
|
2295
|
-
}
|
|
2296
|
-
if (namespace === "trajectory") {
|
|
2297
|
-
if (!object(raw)) fail(errorCode2, `${base}.trajectory must be an object`);
|
|
2298
|
-
if (Object.keys(raw).some((key) => key !== "port" && key !== "themes")) fail(errorCode2, `${base}.trajectory contains an unsupported setting`);
|
|
2299
|
-
if (raw.port !== void 0 && (!positiveInteger(raw.port) || raw.port > 65535)) fail(errorCode2, `${base}.trajectory.port must be an integer from 1 to 65535`);
|
|
2300
|
-
if (raw.themes !== void 0 && typeof raw.themes !== "boolean") fail(errorCode2, `${base}.trajectory.themes must be a boolean`);
|
|
2301
|
-
normalized.trajectory = Object.freeze({ ...raw.port === void 0 ? {} : { port: raw.port }, ...raw.themes === void 0 ? applyDefaults ? { themes: false } : {} : { themes: raw.themes } });
|
|
2302
|
-
continue;
|
|
2303
|
-
}
|
|
2304
|
-
if (!jsonValue(raw)) fail(errorCode2, `${base}.${namespace} must be JSON-compatible`);
|
|
2305
|
-
normalized[namespace] = structuredClone(raw);
|
|
2306
|
-
}
|
|
2307
|
-
return deepFreeze(normalized);
|
|
2308
|
-
}
|
|
2309
|
-
function positiveRetentionInteger(value) {
|
|
2310
|
-
return positiveInteger(value) && Number.isSafeInteger(value);
|
|
2311
|
-
}
|
|
2312
|
-
function validateRetention(value, settingsPath) {
|
|
2313
|
-
if (value === void 0) return void 0;
|
|
2314
|
-
const base = `${settingsPath}.retention`;
|
|
2315
|
-
if (!object(value)) fail("INVALID_SETTINGS", `${base} must be an object`);
|
|
2316
|
-
const unknown = Object.keys(value).find((key) => key !== "olderThanDays" && key !== "maxTerminalRuns");
|
|
2317
|
-
if (unknown) fail("INVALID_SETTINGS", `Unknown retention setting at ${base}: ${unknown}`);
|
|
2318
|
-
if (value.olderThanDays !== void 0 && !positiveRetentionInteger(value.olderThanDays)) fail("INVALID_SETTINGS", `${base}.olderThanDays must be a positive integer`);
|
|
2319
|
-
if (value.maxTerminalRuns !== void 0 && !positiveRetentionInteger(value.maxTerminalRuns)) fail("INVALID_SETTINGS", `${base}.maxTerminalRuns must be a positive integer`);
|
|
2320
|
-
return Object.freeze({ ...value.olderThanDays === void 0 ? {} : { olderThanDays: value.olderThanDays }, ...value.maxTerminalRuns === void 0 ? {} : { maxTerminalRuns: value.maxTerminalRuns } });
|
|
2321
|
-
}
|
|
2322
|
-
function parseSettings(path, partial) {
|
|
2323
|
-
let parsed;
|
|
2324
|
-
try {
|
|
2325
|
-
parsed = JSON.parse(readFileSync2(path, "utf8"));
|
|
2326
|
-
} catch (error) {
|
|
2327
|
-
if (isNodeError(error, "ENOENT")) return partial ? Object.freeze({}) : DEFAULT_SETTINGS;
|
|
2328
|
-
fail("CONFIG_ERROR", `Invalid workflow settings JSON at ${path}: ${errorText(error)}`);
|
|
2329
|
-
}
|
|
2330
|
-
if (!object(parsed)) fail("INVALID_SETTINGS", `Workflow settings at ${path} must be an object`);
|
|
2331
|
-
const allowed = /* @__PURE__ */ new Set(["concurrency", "modelAliases", "skills", "extensions", "extensionSettings", "tools", "retention", "worktreePostCreateCommand", ...partial ? [] : ["backgroundWidget"]]);
|
|
2332
|
-
const unknown = Object.keys(parsed).find((key) => !allowed.has(key));
|
|
2333
|
-
if (Object.prototype.hasOwnProperty.call(parsed, "disabledAgentResources")) fail("INVALID_SETTINGS", `disabledAgentResources is no longer supported; use skills, extensions, and tools selectors (settings: ${path})`);
|
|
2334
|
-
if (unknown) fail("INVALID_SETTINGS", `Unknown workflow setting at ${path}: ${unknown}`);
|
|
2335
|
-
const concurrency = parsed.concurrency === void 0 ? partial ? void 0 : DEFAULT_SETTINGS.concurrency : parsed.concurrency;
|
|
2336
|
-
if (concurrency !== void 0 && (!positiveInteger(concurrency) || concurrency > 16)) fail("INVALID_SETTINGS", `${path}.concurrency must be an integer from 1 to 16`);
|
|
2337
|
-
const backgroundWidget = parsed.backgroundWidget === void 0 ? partial ? void 0 : DEFAULT_SETTINGS.backgroundWidget : parsed.backgroundWidget;
|
|
2338
|
-
if (backgroundWidget !== void 0 && typeof backgroundWidget !== "boolean") fail("INVALID_SETTINGS", `${path}.backgroundWidget must be a boolean`);
|
|
2339
|
-
const modelAliases = parsed.modelAliases === void 0 ? void 0 : validateModelAliases(parsed.modelAliases, path);
|
|
2340
|
-
const skills = validateSelectorList(parsed.skills, path, "skills");
|
|
2341
|
-
const tools = validateSelectorList(parsed.tools, path, "tools");
|
|
2342
|
-
const extensions = validateSelectorList(parsed.extensions, path, "extensions");
|
|
2343
|
-
const extensionSettings = parsed.extensionSettings === void 0 ? void 0 : validateWorkflowExtensionSettings(parsed.extensionSettings, path, "INVALID_SETTINGS", !partial);
|
|
2344
|
-
const retention = validateRetention(parsed.retention, path);
|
|
2345
|
-
const worktreePostCreateCommand = validateWorktreePostCreateCommand(parsed.worktreePostCreateCommand, path);
|
|
2346
|
-
return Object.freeze({
|
|
2347
|
-
...concurrency === void 0 ? {} : { concurrency },
|
|
2348
|
-
...backgroundWidget === void 0 ? {} : { backgroundWidget },
|
|
2349
|
-
...modelAliases === void 0 ? {} : { modelAliases },
|
|
2350
|
-
...skills === void 0 ? {} : { skills },
|
|
2351
|
-
...extensions === void 0 ? {} : { extensions },
|
|
2352
|
-
...extensionSettings === void 0 ? {} : { extensionSettings },
|
|
2353
|
-
...tools === void 0 ? {} : { tools },
|
|
2354
|
-
...retention === void 0 ? {} : { retention },
|
|
2355
|
-
...worktreePostCreateCommand === void 0 ? {} : { worktreePostCreateCommand }
|
|
2356
|
-
});
|
|
2357
|
-
}
|
|
2358
|
-
function loadSettings(path = workflowSettingsPath()) {
|
|
2359
|
-
return parseSettings(path, false);
|
|
2360
|
-
}
|
|
2361
|
-
function loadSettingsOverrides(path) {
|
|
2362
|
-
return parseSettings(path, true);
|
|
2363
|
-
}
|
|
2364
|
-
function resolveWorkflowSettings(cwd, projectTrusted, globalSettingsPath = workflowSettingsPath()) {
|
|
2365
|
-
const projectSettingsPath = workflowProjectSettingsPath(cwd);
|
|
2366
|
-
const global = loadSettings(globalSettingsPath);
|
|
2367
|
-
const project = projectTrusted ? loadSettingsOverrides(projectSettingsPath) : Object.freeze({});
|
|
2368
|
-
const projectHas = (key) => Object.prototype.hasOwnProperty.call(project, key);
|
|
2369
|
-
const sourceFor = (key) => projectHas(key) ? projectSettingsPath : globalSettingsPath;
|
|
2370
|
-
const globalSelectors = selectorsFromSettings(global);
|
|
2371
|
-
const projectSelectors = selectorsFromSettings(project);
|
|
2372
|
-
const effectiveSelectors = selectorSet({
|
|
2373
|
-
skills: [...globalSelectors.skills ?? [], ...projectSelectors.skills ?? []],
|
|
2374
|
-
extensions: [...globalSelectors.extensions ?? [], ...projectSelectors.extensions ?? []],
|
|
2375
|
-
...globalSelectors.tools === void 0 && projectSelectors.tools === void 0 ? {} : { tools: [...globalSelectors.tools ?? [], ...projectSelectors.tools ?? []] }
|
|
2376
|
-
});
|
|
2377
|
-
const hasExtensionSelectors = global.extensions !== void 0 || project.extensions !== void 0;
|
|
2378
|
-
const extensionSettings = projectHas("extensionSettings") ? project.extensionSettings : global.extensionSettings;
|
|
2379
|
-
const sources = {
|
|
2380
|
-
concurrency: projectHas("concurrency") ? projectSettingsPath : globalSettingsPath,
|
|
2381
|
-
modelAliases: projectHas("modelAliases") ? projectSettingsPath : globalSettingsPath,
|
|
2382
|
-
skills: sourceFor("skills"),
|
|
2383
|
-
extensions: sourceFor("extensions"),
|
|
2384
|
-
tools: sourceFor("tools"),
|
|
2385
|
-
...extensionSettings === void 0 ? {} : { extensionSettings: projectHas("extensionSettings") ? projectSettingsPath : globalSettingsPath },
|
|
2386
|
-
...project.retention === void 0 && global.retention === void 0 ? {} : { retention: project.retention === void 0 ? globalSettingsPath : projectSettingsPath },
|
|
2387
|
-
...projectHas("worktreePostCreateCommand") || global.worktreePostCreateCommand !== void 0 ? { worktreePostCreateCommand: projectHas("worktreePostCreateCommand") ? projectSettingsPath : globalSettingsPath } : {}
|
|
2388
|
-
};
|
|
2389
|
-
const effective = Object.freeze({
|
|
2390
|
-
concurrency: project.concurrency ?? global.concurrency,
|
|
2391
|
-
backgroundWidget: global.backgroundWidget ?? true,
|
|
2392
|
-
...projectHas("modelAliases") ? { modelAliases: project.modelAliases } : global.modelAliases === void 0 ? {} : { modelAliases: global.modelAliases },
|
|
2393
|
-
...effectiveSelectors.skills.length ? { skills: effectiveSelectors.skills } : global.skills !== void 0 || project.skills !== void 0 ? { skills: effectiveSelectors.skills } : {},
|
|
2394
|
-
...hasExtensionSelectors ? { extensions: effectiveSelectors.extensions } : {},
|
|
2395
|
-
...extensionSettings === void 0 ? {} : { extensionSettings },
|
|
2396
|
-
...effectiveSelectors.tools?.length ? { tools: effectiveSelectors.tools } : global.tools !== void 0 || project.tools !== void 0 ? { tools: effectiveSelectors.tools } : {},
|
|
2397
|
-
...(project.retention ?? global.retention) === void 0 ? {} : { retention: project.retention ?? global.retention },
|
|
2398
|
-
...projectHas("worktreePostCreateCommand") ? { worktreePostCreateCommand: project.worktreePostCreateCommand } : global.worktreePostCreateCommand === void 0 ? {} : { worktreePostCreateCommand: global.worktreePostCreateCommand }
|
|
2399
|
-
});
|
|
2400
|
-
return { globalSettingsPath, projectSettingsPath, projectTrusted, global, project, effective, sources };
|
|
2401
|
-
}
|
|
2402
|
-
var ROLE_DIRECTORY = "pi-extensible-workflows";
|
|
2403
|
-
function validateSchema(schema, at = "schema") {
|
|
2404
|
-
if (!object(schema) || Object.getPrototypeOf(schema) !== Object.prototype || !jsonValue(schema)) fail("INVALID_SCHEMA", `${at} must be a plain JSON-compatible Schema object`);
|
|
2405
|
-
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`);
|
|
2406
|
-
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`);
|
|
2407
|
-
if (schema.properties !== void 0 && !object(schema.properties)) fail("INVALID_SCHEMA", `${at}.properties must be an object`);
|
|
2408
|
-
}
|
|
2409
|
-
|
|
2410
2435
|
// subagents/src/contracts.ts
|
|
2411
2436
|
import { Value as Value2 } from "typebox/value";
|
|
2412
2437
|
var SUBAGENT_MAX_RETRIES = 255;
|
|
@@ -2431,7 +2456,8 @@ var SUBAGENTS_RUN_PARAMETERS = Type.Object({
|
|
|
2431
2456
|
timeoutMs: Type.Optional(Type.Union([Type.Integer({ minimum: 1, description: "Optional execution timeout in milliseconds" }), Type.Null()]))
|
|
2432
2457
|
}, { additionalProperties: false });
|
|
2433
2458
|
var SUBAGENTS_INSPECT_PARAMETERS = Type.Object({
|
|
2434
|
-
id: Type.Optional(Type.String({ description: "Subagent ID; omit to list ordered run summaries" }))
|
|
2459
|
+
id: Type.Optional(Type.String({ description: "Subagent ID; omit to list ordered run summaries" })),
|
|
2460
|
+
scope: Type.Optional(Type.Union([Type.Literal("session"), Type.Literal("all")], { description: "List scope when id is omitted: session (default) lists runs from the current Pi session; all lists every stored run" }))
|
|
2435
2461
|
}, { additionalProperties: false });
|
|
2436
2462
|
var SUBAGENTS_ID_PARAMETERS = Type.Object({
|
|
2437
2463
|
id: Type.String({ description: "Subagent ID" })
|
|
@@ -2446,6 +2472,24 @@ var SUBAGENTS_STOP_PARAMETERS = Type.Object({
|
|
|
2446
2472
|
var SUBAGENTS_RETRY_PARAMETERS = Type.Object({
|
|
2447
2473
|
id: Type.String({ description: "Failed or stopped subagent ID to retry" })
|
|
2448
2474
|
}, { additionalProperties: false });
|
|
2475
|
+
var SUBAGENT_STATUS_OUTPUT = Type.Object({
|
|
2476
|
+
id: Type.String(),
|
|
2477
|
+
state: Type.String(),
|
|
2478
|
+
sessionId: Type.Optional(Type.String()),
|
|
2479
|
+
startedAt: Type.Optional(Type.Number()),
|
|
2480
|
+
finishedAt: Type.Optional(Type.Number()),
|
|
2481
|
+
attempts: Type.Optional(Type.Number()),
|
|
2482
|
+
attemptDetails: Type.Optional(Type.Array(Type.Unknown())),
|
|
2483
|
+
worktree: Type.Optional(Type.Object({ path: Type.String(), branch: Type.String() })),
|
|
2484
|
+
error: Type.Optional(Type.Object({ code: Type.String(), message: Type.String() })),
|
|
2485
|
+
progress: Type.Optional(Type.Unknown()),
|
|
2486
|
+
value: Type.Optional(Type.Unknown())
|
|
2487
|
+
});
|
|
2488
|
+
var SUBAGENTS_INSPECT_OUTPUT = Type.Union([SUBAGENT_STATUS_OUTPUT, Type.Array(Type.Object({ id: Type.String(), state: Type.String() }))]);
|
|
2489
|
+
var SUBAGENTS_STEER_OUTPUT = Type.Object({ id: Type.String(), accepted: Type.Boolean() });
|
|
2490
|
+
|
|
2491
|
+
// src/tool-timing.ts
|
|
2492
|
+
var TOOL_TIMING_ENTRY_TYPE = "pi-workflows:tool-timing";
|
|
2449
2493
|
|
|
2450
2494
|
// src/trajectory-contracts.ts
|
|
2451
2495
|
var TRAJECTORY_ACTIONS = ["checkpoint-approve", "checkpoint-reject", "pause", "resume", "steer", "stop", "retry", "share"];
|
|
@@ -2478,7 +2522,7 @@ function transcriptToolCallId(value) {
|
|
|
2478
2522
|
return void 0;
|
|
2479
2523
|
}
|
|
2480
2524
|
function isTimingTranscriptEntry(value) {
|
|
2481
|
-
return object(value) && value.type === "custom" && value.customType ===
|
|
2525
|
+
return object(value) && value.type === "custom" && value.customType === TOOL_TIMING_ENTRY_TYPE;
|
|
2482
2526
|
}
|
|
2483
2527
|
function timingToolCallId(value) {
|
|
2484
2528
|
if (!object(value) || !isTimingTranscriptEntry(value) || !object(value.data)) return void 0;
|
|
@@ -2599,21 +2643,21 @@ function withPiToolDescriptions(run) {
|
|
|
2599
2643
|
function canonicalExtensionSelector(selector, base) {
|
|
2600
2644
|
const negated = selector.startsWith("!");
|
|
2601
2645
|
const body = negated ? selector.slice(1) : selector;
|
|
2602
|
-
if (body === "*" || body === "**" || body.startsWith("**/")) return selector;
|
|
2646
|
+
if (body === "*" || body === "**" || body.startsWith("**/") || body.startsWith("builtin:")) return selector;
|
|
2603
2647
|
const resolved = resolve4(base, body);
|
|
2604
2648
|
if (resourcePatternHasMagic(body)) return `${negated ? "!" : ""}${resolved}`;
|
|
2605
2649
|
return `${negated ? "!" : ""}${canonicalPath(resolved)}`;
|
|
2606
2650
|
}
|
|
2607
2651
|
function skillNameFromPath(path) {
|
|
2608
|
-
const file =
|
|
2609
|
-
return file.toLowerCase() === "skill.md" ?
|
|
2652
|
+
const file = basename3(path);
|
|
2653
|
+
return file.toLowerCase() === "skill.md" ? basename3(dirname4(path)) : file;
|
|
2610
2654
|
}
|
|
2611
2655
|
var discoveredResourcesByCwd = /* @__PURE__ */ new Map();
|
|
2612
2656
|
async function discoveredResources(cwd) {
|
|
2613
2657
|
const cached = discoveredResourcesByCwd.get(cwd);
|
|
2614
2658
|
if (cached) return cached;
|
|
2615
2659
|
const pending = (async () => {
|
|
2616
|
-
const agentDir =
|
|
2660
|
+
const agentDir = getAgentDir3();
|
|
2617
2661
|
const settingsManager = SettingsManager.create(cwd, agentDir, { projectTrusted: false });
|
|
2618
2662
|
const packageManager = new DefaultPackageManager({ cwd, agentDir, settingsManager });
|
|
2619
2663
|
const resolved = await packageManager.resolve();
|
|
@@ -2660,11 +2704,62 @@ function withoutAgentActivities(run) {
|
|
|
2660
2704
|
});
|
|
2661
2705
|
return agents.every((agent, index) => agent === run.agents[index]) ? run : { ...run, agents };
|
|
2662
2706
|
}
|
|
2707
|
+
function serializedJsonBytes(value) {
|
|
2708
|
+
const serialized = JSON.stringify(value);
|
|
2709
|
+
return Buffer.byteLength(serialized, "utf8");
|
|
2710
|
+
}
|
|
2711
|
+
function failedAgentOutput(agent) {
|
|
2712
|
+
const error = [...agent.attemptDetails ?? []].reverse().find((attempt) => attempt.error)?.error;
|
|
2713
|
+
const status = agent.state === "cancelled" ? "cancelled" : "failed";
|
|
2714
|
+
return { status, code: error?.code ?? (status === "cancelled" ? "CANCELLED" : "AGENT_FAILED"), message: error?.message ?? `Agent ${agent.state}` };
|
|
2715
|
+
}
|
|
2716
|
+
function operationOutput(operation) {
|
|
2717
|
+
const bytes = serializedJsonBytes(operation.value);
|
|
2718
|
+
return bytes > DEFAULT_MAX_BYTES ? { status: "truncated", kind: "result", bytes } : { status: "available", value: operation.value, bytes };
|
|
2719
|
+
}
|
|
2720
|
+
function outputForCall(call, operation) {
|
|
2721
|
+
if (operation !== void 0) return call.kind === "tool" ? { status: "completed" } : operationOutput(operation);
|
|
2722
|
+
if (call.error) return { status: call.error.code === "CANCELLED" ? "cancelled" : "failed", code: call.error.code, ...call.kind === "shell" ? { message: call.error.message ?? "Shell failed" } : {} };
|
|
2723
|
+
return call.finishedAt === void 0 ? { status: "pending" } : { status: "unavailable" };
|
|
2724
|
+
}
|
|
2725
|
+
function outputForAgent(agent, operation) {
|
|
2726
|
+
if (operation !== void 0) return operationOutput(operation);
|
|
2727
|
+
if (agent.state === "failed" || agent.state === "cancelled") return failedAgentOutput(agent);
|
|
2728
|
+
if (agent.state === "completed") return { status: "unavailable" };
|
|
2729
|
+
return { status: "pending" };
|
|
2730
|
+
}
|
|
2731
|
+
function applyTrajectoryAgentOutputs(run, operations) {
|
|
2732
|
+
const byPath = new Map(operations.map((operation) => [operation.path, operation]));
|
|
2733
|
+
const agents = run.agents.map((agent) => ({ ...agent, output: outputForAgent(agent, agent.resultPath === void 0 ? void 0 : byPath.get(agent.resultPath)) }));
|
|
2734
|
+
const scriptCalls = run.scriptCalls?.map((call) => {
|
|
2735
|
+
const recorded = { ...call, output: outputForCall(call, byPath.get(call.path)) };
|
|
2736
|
+
if (call.kind === "tool") {
|
|
2737
|
+
delete recorded.input;
|
|
2738
|
+
delete recorded.inputBytes;
|
|
2739
|
+
if (call.error) recorded.error = { code: call.error.code };
|
|
2740
|
+
}
|
|
2741
|
+
return recorded;
|
|
2742
|
+
});
|
|
2743
|
+
const rest = run;
|
|
2744
|
+
return { ...rest, agents, ...scriptCalls === void 0 ? {} : { scriptCalls } };
|
|
2745
|
+
}
|
|
2746
|
+
function overlayTrajectoryRun(run, overlay) {
|
|
2747
|
+
const overlaid = overlay(run);
|
|
2748
|
+
const outputs = new Map(run.agents.map((agent) => [agent.id, agent.output]));
|
|
2749
|
+
const agents = overlaid.agents.map((agent) => {
|
|
2750
|
+
const previous = outputs.get(agent.id);
|
|
2751
|
+
const output = previous?.status === "available" || previous?.status === "truncated" ? previous : outputForAgent(agent, void 0);
|
|
2752
|
+
return { ...agent, output };
|
|
2753
|
+
});
|
|
2754
|
+
return { ...overlaid, agents, ...run.scriptCalls === void 0 ? {} : { scriptCalls: run.scriptCalls } };
|
|
2755
|
+
}
|
|
2663
2756
|
async function loadTrajectoryRun(store, includeTranscripts = true) {
|
|
2664
2757
|
const value = await store.load();
|
|
2665
2758
|
const summary = await store.loadSummary().catch(() => void 0);
|
|
2666
2759
|
const prompts = await store.systemPrompts().catch(() => []);
|
|
2667
|
-
const
|
|
2760
|
+
const prepared = withoutAgentActivities(await withResolvedResources(withPiToolDescriptions(applySystemPrompts(value.run, prompts)), store.cwd));
|
|
2761
|
+
const operations = await store.replayableOperations().catch(() => []);
|
|
2762
|
+
const run = applyTrajectoryAgentOutputs(prepared, operations);
|
|
2668
2763
|
return { run, snapshot: value.snapshot, awaiting: await store.awaitingCheckpoints(), ...summary?.createdAt === void 0 ? {} : { createdAt: summary.createdAt }, transcripts: includeTranscripts ? await runTranscripts(run) : {} };
|
|
2669
2764
|
}
|
|
2670
2765
|
async function loadTrajectoryRuns(cwd, sessionId, home = homedir5(), overlay) {
|
|
@@ -2672,7 +2767,7 @@ async function loadTrajectoryRuns(cwd, sessionId, home = homedir5(), overlay) {
|
|
|
2672
2767
|
for (const runId of await listRunIds(cwd, sessionId, home, false)) {
|
|
2673
2768
|
try {
|
|
2674
2769
|
const value = await loadTrajectoryRun(new RunStore(cwd, sessionId, runId, home));
|
|
2675
|
-
loaded.push(overlay ? { ...value, run:
|
|
2770
|
+
loaded.push(overlay ? { ...value, run: overlayTrajectoryRun(value.run, overlay) } : value);
|
|
2676
2771
|
} catch {
|
|
2677
2772
|
}
|
|
2678
2773
|
}
|
|
@@ -2695,15 +2790,16 @@ async function exportTrajectoryRunHtml(options) {
|
|
|
2695
2790
|
const run = runs.find((candidate) => candidate.run.id === options.runId);
|
|
2696
2791
|
if (!run) throw new Error(`Workflow run ${options.runId} was not found for session ${options.sessionId}`);
|
|
2697
2792
|
const assets = new URL("./assets/", import.meta.url);
|
|
2698
|
-
const [html, marked, morphdom, favicon] = await Promise.all([
|
|
2793
|
+
const [html, marked, morphdom, prism, favicon] = await Promise.all([
|
|
2699
2794
|
readFile5(new URL("index.html", assets), "utf8"),
|
|
2700
2795
|
readFile5(new URL("marked.min.js", assets)),
|
|
2701
2796
|
readFile5(new URL("morphdom.min.js", assets)),
|
|
2797
|
+
readFile5(new URL("prism.min.js", assets)),
|
|
2702
2798
|
readFile5(new URL("favicon.png", assets))
|
|
2703
2799
|
]);
|
|
2704
2800
|
const state = {
|
|
2705
2801
|
type: "state",
|
|
2706
|
-
publishers: [{ id: `export-${options.runId}`, title: `run ${options.runId.slice(0, 8)}`, cwd: options.cwd, sessionId: options.sessionId,
|
|
2802
|
+
publishers: [{ id: `export-${options.runId}`, title: `run ${options.runId.slice(0, 8)}`, cwd: options.cwd, sessionId: options.sessionId, connected: true, runs: [run], subagents: [] }],
|
|
2707
2803
|
updatedAt: Date.now()
|
|
2708
2804
|
};
|
|
2709
2805
|
const script = (source) => `<script src="data:text/javascript;base64,${source.toString("base64")}"></script>`;
|
|
@@ -2711,6 +2807,7 @@ async function exportTrajectoryRunHtml(options) {
|
|
|
2711
2807
|
output = inlineAsset(output, '<link rel="icon" type="image/png" href="./favicon.png" />', `<link rel="icon" type="image/png" href="data:image/png;base64,${favicon.toString("base64")}" />`);
|
|
2712
2808
|
output = inlineAsset(output, '<script src="./marked.min.js"></script>', script(marked));
|
|
2713
2809
|
output = inlineAsset(output, '<script src="./morphdom.min.js"></script>', script(morphdom));
|
|
2810
|
+
output = inlineAsset(output, '<script src="./prism.min.js"></script>', script(prism));
|
|
2714
2811
|
output = inlineAsset(output, '<body data-view="run">', `<body data-view="run">
|
|
2715
2812
|
<script>window.__PIEWF_STATIC__ = ${JSON.stringify(state).replace(/</g, "\\u003c")};</script>`);
|
|
2716
2813
|
return output;
|
|
@@ -2771,14 +2868,20 @@ function trajectoryPort(value) {
|
|
|
2771
2868
|
function delay(ms) {
|
|
2772
2869
|
return new Promise((resolve5) => setTimeout(resolve5, ms));
|
|
2773
2870
|
}
|
|
2774
|
-
async function
|
|
2871
|
+
async function serverHealth(port) {
|
|
2775
2872
|
try {
|
|
2776
2873
|
const response = await fetch(`http://127.0.0.1:${String(port)}/health`, { signal: AbortSignal.timeout(300) });
|
|
2777
|
-
|
|
2874
|
+
if (!response.ok) return void 0;
|
|
2875
|
+
const body = await response.json().catch(() => void 0);
|
|
2876
|
+
if (!object(body)) return {};
|
|
2877
|
+
return { ...positiveInteger(body.pid) ? { pid: body.pid } : {}, ...typeof body.fingerprint === "string" ? { fingerprint: body.fingerprint } : {}, ...positiveInteger(body.startedAt) ? { startedAt: body.startedAt } : {} };
|
|
2778
2878
|
} catch {
|
|
2779
|
-
return
|
|
2879
|
+
return void 0;
|
|
2780
2880
|
}
|
|
2781
2881
|
}
|
|
2882
|
+
async function serverHealthy(port) {
|
|
2883
|
+
return await serverHealth(port) !== void 0;
|
|
2884
|
+
}
|
|
2782
2885
|
function signalProcess(pid, signal) {
|
|
2783
2886
|
try {
|
|
2784
2887
|
process.kill(pid, signal);
|
|
@@ -2811,23 +2914,27 @@ async function readLock(path) {
|
|
|
2811
2914
|
return void 0;
|
|
2812
2915
|
}
|
|
2813
2916
|
}
|
|
2814
|
-
async function waitForServer(port) {
|
|
2917
|
+
async function waitForServer(port, fingerprint) {
|
|
2918
|
+
let foreign = false;
|
|
2815
2919
|
for (let attempt = 0; attempt < 60; attempt += 1) {
|
|
2816
|
-
|
|
2920
|
+
const health = await serverHealth(port);
|
|
2921
|
+
if (health?.fingerprint === fingerprint) return;
|
|
2922
|
+
foreign = health !== void 0;
|
|
2817
2923
|
await delay(50);
|
|
2818
2924
|
}
|
|
2819
|
-
throw new Error(`Trajectory server did not start on port ${String(port)}`);
|
|
2925
|
+
throw new Error(foreign ? `Trajectory port ${String(port)} is held by another Trajectory server; stop it and retry` : `Trajectory server did not start on port ${String(port)}`);
|
|
2820
2926
|
}
|
|
2821
2927
|
async function resolveExistingServer(lockPath, existing, fingerprint) {
|
|
2822
|
-
|
|
2823
|
-
|
|
2928
|
+
const health = await serverHealth(existing.port);
|
|
2929
|
+
if (health) {
|
|
2930
|
+
if (existing.fingerprint === fingerprint && health.fingerprint === fingerprint) return existing;
|
|
2824
2931
|
await stopStaleServer(existing);
|
|
2825
2932
|
await rm5(lockPath, { force: true });
|
|
2826
2933
|
return void 0;
|
|
2827
2934
|
}
|
|
2828
2935
|
if (await processAlive(existing.pid, existing.startedAt)) {
|
|
2829
2936
|
try {
|
|
2830
|
-
await waitForServer(existing.port);
|
|
2937
|
+
await waitForServer(existing.port, fingerprint);
|
|
2831
2938
|
return existing;
|
|
2832
2939
|
} catch {
|
|
2833
2940
|
await stopStaleServer(existing);
|
|
@@ -2870,13 +2977,15 @@ async function ensureTrajectoryServer(agentDir, configuredPort) {
|
|
|
2870
2977
|
} finally {
|
|
2871
2978
|
await lockHandle?.close();
|
|
2872
2979
|
}
|
|
2980
|
+
const occupant = await serverHealth(configuredPort);
|
|
2981
|
+
if (occupant?.pid !== void 0 && occupant.fingerprint !== fingerprint) await stopStaleServer({ pid: occupant.pid, port: configuredPort, ...occupant.startedAt === void 0 ? {} : { startedAt: occupant.startedAt } });
|
|
2873
2982
|
try {
|
|
2874
2983
|
const child = spawn(process.versions.bun ? "node" : process.execPath, [serverPath, "--port", String(configuredPort), "--lock", lockPath, "--fingerprint", fingerprint], { detached: true, stdio: "ignore" });
|
|
2875
2984
|
const startupError = new Promise((_resolve, reject) => {
|
|
2876
2985
|
child.once("error", reject);
|
|
2877
2986
|
});
|
|
2878
2987
|
child.unref();
|
|
2879
|
-
await Promise.race([waitForServer(configuredPort), startupError]);
|
|
2988
|
+
await Promise.race([waitForServer(configuredPort, fingerprint), startupError]);
|
|
2880
2989
|
return { port: configuredPort };
|
|
2881
2990
|
} catch (error) {
|
|
2882
2991
|
await rm5(lockPath, { force: true });
|
|
@@ -2907,11 +3016,10 @@ var MAX_FRAME_BYTES = 32 * 1024 * 1024;
|
|
|
2907
3016
|
var MAX_LIVE_STATE_BYTES = MAX_FRAME_BYTES - 1024;
|
|
2908
3017
|
var MAX_TRANSCRIPT_REQUESTS = 64;
|
|
2909
3018
|
var TRANSCRIPT_REQUEST_TIMEOUT_MS = 1e4;
|
|
3019
|
+
var ACTIVE_POLL_MS = 1e3;
|
|
3020
|
+
var IDLE_POLL_MS = 1e4;
|
|
2910
3021
|
var RECONNECT_INITIAL_DELAY_MS = 100;
|
|
2911
3022
|
var RECONNECT_MAX_DELAY_MS = 5e3;
|
|
2912
|
-
function isTimingEntry(value) {
|
|
2913
|
-
return object(value) && value.type === "custom" && value.customType === "pi-workflows:tool-timing";
|
|
2914
|
-
}
|
|
2915
3023
|
function boundedString(value, maxLength = 1024) {
|
|
2916
3024
|
if (typeof value !== "string") return value;
|
|
2917
3025
|
const bytes = Buffer.from(value);
|
|
@@ -2921,46 +3029,91 @@ var MAX_LIVE_STRING_BYTES = 64 * 1024;
|
|
|
2921
3029
|
var MAX_LIVE_ARRAY_ENTRIES = 256;
|
|
2922
3030
|
var MAX_LIVE_OBJECT_KEYS = 64;
|
|
2923
3031
|
var LIVE_METADATA_ARRAY_KEYS = /* @__PURE__ */ new Set(["agents", "runs", "subagents"]);
|
|
2924
|
-
var LIVE_TRUNCATABLE_ARRAY_KEYS = /* @__PURE__ */ new Set(["events", "phaseHistory"]);
|
|
3032
|
+
var LIVE_TRUNCATABLE_ARRAY_KEYS = /* @__PURE__ */ new Set(["events", "phaseHistory", "scriptCalls"]);
|
|
2925
3033
|
var LIVE_METADATA_OBJECT_KEYS = /* @__PURE__ */ new Set(["transcripts"]);
|
|
3034
|
+
function liveValueWillBeBounded(value, key = "", depth = 0) {
|
|
3035
|
+
if (typeof value === "string") return Buffer.byteLength(value) > MAX_LIVE_STRING_BYTES;
|
|
3036
|
+
if (typeof value !== "object" || value === null) return false;
|
|
3037
|
+
if (depth >= 12) return true;
|
|
3038
|
+
if (Array.isArray(value)) {
|
|
3039
|
+
const maxEntries = LIVE_TRUNCATABLE_ARRAY_KEYS.has(key) ? MAX_LIVE_ARRAY_ENTRIES - 1 : MAX_LIVE_ARRAY_ENTRIES;
|
|
3040
|
+
if (key === "attemptDetails" && value.length > 8 || !LIVE_METADATA_ARRAY_KEYS.has(key) && value.length > maxEntries) return true;
|
|
3041
|
+
return value.some((entry) => liveValueWillBeBounded(entry, "", depth + 1));
|
|
3042
|
+
}
|
|
3043
|
+
if (!object(value)) return false;
|
|
3044
|
+
const properties = LIVE_METADATA_OBJECT_KEYS.has(key) ? Object.keys(value) : Object.keys(value).sort().slice(0, MAX_LIVE_OBJECT_KEYS);
|
|
3045
|
+
if (!LIVE_METADATA_OBJECT_KEYS.has(key) && Object.keys(value).length > MAX_LIVE_OBJECT_KEYS) return true;
|
|
3046
|
+
return properties.some((property) => liveValueWillBeBounded(value[property], property, depth + 1));
|
|
3047
|
+
}
|
|
3048
|
+
var MAX_LIVE_TIMING_BYTES = 64 * 1024;
|
|
2926
3049
|
function boundedTiming(value) {
|
|
2927
|
-
const entries = Array.isArray(value) ? value.filter(
|
|
3050
|
+
const entries = Array.isArray(value) ? value.filter(isTimingTranscriptEntry) : [];
|
|
2928
3051
|
const retained = [];
|
|
2929
3052
|
let bytes = 2;
|
|
2930
|
-
for (
|
|
3053
|
+
for (let index = entries.length - 1; index >= 0; index -= 1) {
|
|
2931
3054
|
let serialized;
|
|
2932
3055
|
try {
|
|
2933
|
-
serialized = JSON.stringify(
|
|
3056
|
+
serialized = JSON.stringify(entries[index]);
|
|
2934
3057
|
} catch {
|
|
2935
3058
|
continue;
|
|
2936
3059
|
}
|
|
2937
3060
|
const nextBytes = bytes + (retained.length ? 1 : 0) + Buffer.byteLength(serialized);
|
|
2938
|
-
if (nextBytes >=
|
|
2939
|
-
retained.push(
|
|
3061
|
+
if (nextBytes >= MAX_LIVE_TIMING_BYTES) break;
|
|
3062
|
+
retained.push(entries[index]);
|
|
2940
3063
|
bytes = nextBytes;
|
|
2941
3064
|
}
|
|
2942
|
-
return retained;
|
|
3065
|
+
return retained.reverse();
|
|
2943
3066
|
}
|
|
2944
|
-
function boundedLiveValue(value, key = "", depth = 0) {
|
|
2945
|
-
|
|
2946
|
-
if (
|
|
2947
|
-
|
|
2948
|
-
|
|
2949
|
-
|
|
2950
|
-
|
|
2951
|
-
|
|
2952
|
-
|
|
2953
|
-
|
|
2954
|
-
|
|
2955
|
-
|
|
2956
|
-
|
|
2957
|
-
|
|
2958
|
-
|
|
2959
|
-
|
|
2960
|
-
|
|
2961
|
-
return
|
|
3067
|
+
function boundedLiveValue(value, key = "", depth = 0, bounds) {
|
|
3068
|
+
const pathLength = bounds?.path.length ?? 0;
|
|
3069
|
+
if (bounds && key) bounds.path.push(key);
|
|
3070
|
+
const argsValue = bounds !== void 0 && bounds.path.length >= 3 && bounds.path[0] === "runs" && bounds.path[1] === "snapshot" && bounds.path[2] === "args";
|
|
3071
|
+
const markArgsTruncated = () => {
|
|
3072
|
+
if (argsValue && bounds.runIndex !== void 0) bounds.argsTruncatedRuns.add(bounds.runIndex);
|
|
3073
|
+
};
|
|
3074
|
+
try {
|
|
3075
|
+
if (typeof value === "string") {
|
|
3076
|
+
if (argsValue && Buffer.byteLength(value) > MAX_LIVE_STRING_BYTES) markArgsTruncated();
|
|
3077
|
+
return boundedString(value, MAX_LIVE_STRING_BYTES);
|
|
3078
|
+
}
|
|
3079
|
+
if (typeof value === "number" || typeof value === "boolean" || value === null) return value;
|
|
3080
|
+
if (depth >= 12) {
|
|
3081
|
+
markArgsTruncated();
|
|
3082
|
+
return void 0;
|
|
3083
|
+
}
|
|
3084
|
+
if (key === "timing" && !argsValue) return boundedTiming(value);
|
|
3085
|
+
if (key === "output" && object(value) && value.status === "available") {
|
|
3086
|
+
const boundedOutput = boundedLiveValue(value.value, "value", depth + 1, bounds);
|
|
3087
|
+
return liveValueWillBeBounded(value.value, "value", depth + 1) ? { status: "truncated", kind: "result", bytes: value.bytes } : { ...value, value: boundedOutput };
|
|
3088
|
+
}
|
|
3089
|
+
if (Array.isArray(value)) {
|
|
3090
|
+
const array = value;
|
|
3091
|
+
const maxEntries = !argsValue && LIVE_TRUNCATABLE_ARRAY_KEYS.has(key) ? MAX_LIVE_ARRAY_ENTRIES - 1 : MAX_LIVE_ARRAY_ENTRIES;
|
|
3092
|
+
const entries = !argsValue && key === "attemptDetails" ? array.slice(-8) : !argsValue && LIVE_METADATA_ARRAY_KEYS.has(key) ? array : array.length <= maxEntries ? array : [...array.slice(0, 32), ...array.slice(-maxEntries + 32)];
|
|
3093
|
+
if (entries.length !== array.length) markArgsTruncated();
|
|
3094
|
+
const publisherRuns = bounds?.path.length === 1 && bounds.path[0] === "runs";
|
|
3095
|
+
const bounded = entries.map((entry, index) => {
|
|
3096
|
+
const previousRunIndex = bounds?.runIndex;
|
|
3097
|
+
if (publisherRuns) bounds.runIndex = index;
|
|
3098
|
+
const result = boundedLiveValue(entry, "", depth + 1, bounds);
|
|
3099
|
+
if (bounds) bounds.runIndex = previousRunIndex;
|
|
3100
|
+
return result;
|
|
3101
|
+
});
|
|
3102
|
+
if (entries.length !== array.length && LIVE_TRUNCATABLE_ARRAY_KEYS.has(key)) bounded.push({ type: "trajectory:truncated", field: key, omitted: array.length - entries.length });
|
|
3103
|
+
return bounded;
|
|
3104
|
+
}
|
|
3105
|
+
if (object(value)) {
|
|
3106
|
+
const result = {};
|
|
3107
|
+
const keys = Object.keys(value).sort();
|
|
3108
|
+
const properties = !argsValue && LIVE_METADATA_OBJECT_KEYS.has(key) ? keys : keys.slice(0, MAX_LIVE_OBJECT_KEYS);
|
|
3109
|
+
if (properties.length !== keys.length) markArgsTruncated();
|
|
3110
|
+
for (const property of properties) result[property] = boundedLiveValue(value[property], property, depth + 1, bounds);
|
|
3111
|
+
return result;
|
|
3112
|
+
}
|
|
3113
|
+
return void 0;
|
|
3114
|
+
} finally {
|
|
3115
|
+
if (bounds) bounds.path.length = pathLength;
|
|
2962
3116
|
}
|
|
2963
|
-
return void 0;
|
|
2964
3117
|
}
|
|
2965
3118
|
function transcriptRevisionProjection(value, depth = 0) {
|
|
2966
3119
|
if (depth >= 8) return typeof value === "string" ? boundedString(value, 1024) : typeof value === "number" || typeof value === "boolean" || value === null ? value : void 0;
|
|
@@ -3007,7 +3160,19 @@ function projectPublisher(metadata, publisher, revisions) {
|
|
|
3007
3160
|
return { ...publisher, runs: metadata.runs.map((run) => projectRun(run, `${publisherId2} ${run.run.id}`, revisions)), subagents: metadata.subagents.map((subagent) => projectSubagent(subagent, `${publisherId2} subagent ${subagent.id}`, revisions)) };
|
|
3008
3161
|
}
|
|
3009
3162
|
function minimalStatePublisher(publisher) {
|
|
3010
|
-
|
|
3163
|
+
const bounds = { argsTruncatedRuns: /* @__PURE__ */ new Set(), path: [], runIndex: void 0 };
|
|
3164
|
+
const bounded = boundedLiveValue(publisher, "", 0, bounds) ?? {};
|
|
3165
|
+
const sourceRuns = Array.isArray(publisher.runs) ? publisher.runs : [];
|
|
3166
|
+
const boundedRuns = Array.isArray(bounded.runs) ? bounded.runs : [];
|
|
3167
|
+
if (!sourceRuns.length || !boundedRuns.length) return bounded;
|
|
3168
|
+
const runs = boundedRuns.map((run, index) => {
|
|
3169
|
+
const sourceRun = sourceRuns[index];
|
|
3170
|
+
const sourceSnapshot = object(sourceRun) && object(sourceRun.snapshot) ? sourceRun.snapshot : void 0;
|
|
3171
|
+
const boundedSnapshot = object(run) && object(run.snapshot) ? run.snapshot : void 0;
|
|
3172
|
+
const argsUnavailable = sourceSnapshot !== void 0 && sourceSnapshot.args !== void 0 && sourceSnapshot.args !== null && (boundedSnapshot === void 0 || !Object.prototype.hasOwnProperty.call(boundedSnapshot, "args"));
|
|
3173
|
+
return (bounds.argsTruncatedRuns.has(index) || argsUnavailable) && object(run) ? { ...run, snapshotArgsTruncated: true } : run;
|
|
3174
|
+
});
|
|
3175
|
+
return { ...bounded, runs };
|
|
3011
3176
|
}
|
|
3012
3177
|
function publisherStateFrame(publisher, runs, subagents, truncated = false) {
|
|
3013
3178
|
const publisherSummary = { ...publisher };
|
|
@@ -3077,18 +3242,19 @@ function createTrajectoryController(agentDir) {
|
|
|
3077
3242
|
for (const request of pendingTranscripts.values()) clearTimeout(request.timer);
|
|
3078
3243
|
pendingTranscripts.clear();
|
|
3079
3244
|
};
|
|
3245
|
+
let viewers = 0;
|
|
3080
3246
|
const startPolling = () => {
|
|
3081
3247
|
stopPolling();
|
|
3082
3248
|
pollTimer = setInterval(() => {
|
|
3083
3249
|
void sendState().catch((error) => {
|
|
3084
3250
|
if (!closing) console.error(`Trajectory state publish failed: ${errorText(error)}`);
|
|
3085
3251
|
});
|
|
3086
|
-
},
|
|
3252
|
+
}, viewers > 0 ? ACTIVE_POLL_MS : IDLE_POLL_MS);
|
|
3087
3253
|
pollTimer.unref();
|
|
3088
3254
|
};
|
|
3089
3255
|
let stateLoad;
|
|
3090
3256
|
let lastState;
|
|
3091
|
-
const publisherValue = (input) => ({ id: publisherId(input.cwd, input.sessionId), title: `session ${input.sessionId.slice(0, 8)}`, cwd: input.cwd, sessionId: input.sessionId,
|
|
3257
|
+
const publisherValue = (input) => ({ id: publisherId(input.cwd, input.sessionId), title: `session ${input.sessionId.slice(0, 8)}`, cwd: input.cwd, sessionId: input.sessionId, connected: true });
|
|
3092
3258
|
const sendState = async () => {
|
|
3093
3259
|
const activeSocket = socket;
|
|
3094
3260
|
if (activeSocket !== void 0 && stateLoad?.socket === activeSocket) return stateLoad.task;
|
|
@@ -3130,22 +3296,15 @@ function createTrajectoryController(agentDir) {
|
|
|
3130
3296
|
}
|
|
3131
3297
|
};
|
|
3132
3298
|
const transcriptFromFallback = async (input, request) => {
|
|
3133
|
-
const
|
|
3134
|
-
try {
|
|
3135
|
-
if (result.status === "available" && Buffer.byteLength(JSON.stringify(result.entries)) > TRAJECTORY_MAX_TRANSCRIPT_BYTES) return { status: "oversized", revision: result.revision, entries: [], error: "Transcript is too large" };
|
|
3136
|
-
} catch {
|
|
3137
|
-
return { status: "failed", revision: result.revision, entries: [], error: "Transcript failed" };
|
|
3138
|
-
}
|
|
3139
|
-
return result;
|
|
3140
|
-
};
|
|
3141
|
-
const boundedResult = (entries, revision) => {
|
|
3299
|
+
const oversized = (entries, revision) => {
|
|
3142
3300
|
try {
|
|
3143
|
-
|
|
3301
|
+
return Buffer.byteLength(JSON.stringify(entries)) > TRAJECTORY_MAX_TRANSCRIPT_BYTES ? { status: "oversized", revision, entries: [], error: "Transcript is too large" } : void 0;
|
|
3144
3302
|
} catch {
|
|
3145
3303
|
return { status: "failed", revision, entries: [], error: "Transcript failed" };
|
|
3146
3304
|
}
|
|
3147
|
-
return { status: entries.length ? "available" : "empty", revision, entries };
|
|
3148
3305
|
};
|
|
3306
|
+
const boundedTranscriptResult = (result) => (result.status === "available" ? oversized(result.entries, result.revision) : void 0) ?? result;
|
|
3307
|
+
const boundedResult = (entries, revision) => oversized(entries, revision) ?? { status: entries.length ? "available" : "empty", revision, entries };
|
|
3149
3308
|
if (input.loadTranscript) {
|
|
3150
3309
|
const result = await input.loadTranscript(request);
|
|
3151
3310
|
const revisionChanged = request.revision !== void 0 && result.revision !== request.revision && result.status !== "missing" && result.status !== "failed" && result.status !== "oversized" && result.status !== "disconnected";
|
|
@@ -3244,6 +3403,14 @@ function createTrajectoryController(agentDir) {
|
|
|
3244
3403
|
established = false;
|
|
3245
3404
|
return;
|
|
3246
3405
|
}
|
|
3406
|
+
if (message.type === "publisher:viewers" && typeof message.count === "number") {
|
|
3407
|
+
const watched = viewers > 0;
|
|
3408
|
+
viewers = message.count;
|
|
3409
|
+
if (watched === viewers > 0) return;
|
|
3410
|
+
if (socket === next && !closing) startPolling();
|
|
3411
|
+
if (viewers > 0) void sendState().catch(() => void 0);
|
|
3412
|
+
return;
|
|
3413
|
+
}
|
|
3247
3414
|
if (message.type === "publisher:transcript" && typeof message.requestId === "string" && typeof message.publisherId === "string" && (typeof message.runId === "string" && typeof message.agentId === "string" || typeof message.subagentId === "string")) {
|
|
3248
3415
|
handleTranscriptRequest(next, generation, message, input);
|
|
3249
3416
|
return;
|
|
@@ -3398,7 +3565,7 @@ function createTrajectoryController(agentDir) {
|
|
|
3398
3565
|
};
|
|
3399
3566
|
}
|
|
3400
3567
|
function registerTrajectoryExtension(pi, options = {}) {
|
|
3401
|
-
const controller = options.controller ?? createTrajectoryController(options.agentDir ??
|
|
3568
|
+
const controller = options.controller ?? createTrajectoryController(options.agentDir ?? getAgentDir4());
|
|
3402
3569
|
const openUrl = options.openUrl ?? openTrajectoryUrl;
|
|
3403
3570
|
let autoAttached = false;
|
|
3404
3571
|
const notify = (context, message, level) => {
|
|
@@ -3450,6 +3617,7 @@ export {
|
|
|
3450
3617
|
createTrajectoryController,
|
|
3451
3618
|
extension as default,
|
|
3452
3619
|
exportTrajectoryRunHtml,
|
|
3620
|
+
minimalStatePublisher,
|
|
3453
3621
|
openTrajectoryUrl,
|
|
3454
3622
|
registerTrajectoryExtension,
|
|
3455
3623
|
shareTrajectoryRun,
|