@marcoscale98/pi-extensible-workflows 5.14.1-fork.1 → 5.17.0-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.
Files changed (71) hide show
  1. package/CHANGELOG.md +67 -0
  2. package/dist/src/agent-execution.js +36 -51
  3. package/dist/src/background-widget.d.ts +56 -0
  4. package/dist/src/background-widget.js +22 -15
  5. package/dist/src/decoders.js +2 -1
  6. package/dist/src/host-navigator.d.ts +18 -0
  7. package/dist/src/host-navigator.js +71 -13
  8. package/dist/src/host-view.d.ts +16 -1
  9. package/dist/src/host-view.js +109 -42
  10. package/dist/src/host.js +57 -24
  11. package/dist/src/index.d.ts +3 -1
  12. package/dist/src/index.js +867 -545
  13. package/dist/src/index.js.map +3 -3
  14. package/dist/src/registry.js +2 -1
  15. package/dist/src/roles.d.ts +70 -0
  16. package/dist/src/roles.js +325 -0
  17. package/dist/src/settings.d.ts +13 -0
  18. package/dist/src/settings.js +253 -0
  19. package/dist/src/trajectory.d.ts +29 -4
  20. package/dist/src/trajectory.js +58 -3
  21. package/dist/src/types.d.ts +0 -1
  22. package/dist/src/utils.d.ts +2 -1
  23. package/dist/src/utils.js +10 -0
  24. package/dist/src/validation.d.ts +5 -17
  25. package/dist/src/validation.js +9 -458
  26. package/dist/subagents/index.js +1132 -694
  27. package/dist/subagents/index.js.map +3 -3
  28. package/dist/subagents/src/contracts.d.ts +4 -0
  29. package/dist/subagents/src/index.d.ts +1 -1
  30. package/dist/subagents/src/index.js +2 -1
  31. package/dist/subagents/src/manager.js +29 -10
  32. package/dist/subagents/src/navigator.js +396 -247
  33. package/dist/subagents/src/view.d.ts +23 -2
  34. package/dist/subagents/src/view.js +96 -30
  35. package/dist/trajectory/assets/index.html +347 -231
  36. package/dist/trajectory/assets/prism.min.js +13 -0
  37. package/dist/trajectory/index.js +377 -257
  38. package/dist/trajectory/index.js.map +3 -3
  39. package/dist/trajectory/src/assets/index.html +347 -231
  40. package/dist/trajectory/src/assets/prism.min.js +13 -0
  41. package/dist/trajectory/src/export.js +4 -2
  42. package/dist/trajectory/src/index.d.ts +2 -0
  43. package/dist/trajectory/src/index.js +143 -42
  44. package/dist/trajectory/src/server.js +134 -9
  45. package/dist/trajectory/src/server.js.map +1 -1
  46. package/package.json +2 -1
  47. package/src/agent-execution.ts +36 -42
  48. package/src/background-widget.ts +25 -18
  49. package/src/decoders.ts +2 -1
  50. package/src/host-navigator.ts +58 -14
  51. package/src/host-view.ts +93 -37
  52. package/src/host.ts +51 -21
  53. package/src/index.ts +3 -1
  54. package/src/registry.ts +2 -1
  55. package/src/roles.ts +326 -0
  56. package/src/settings.ts +206 -0
  57. package/src/trajectory.ts +76 -7
  58. package/src/types.ts +1 -1
  59. package/src/utils.ts +8 -1
  60. package/src/validation.ts +12 -356
  61. package/subagents/README.md +1 -1
  62. package/subagents/src/contracts.ts +4 -0
  63. package/subagents/src/index.ts +3 -2
  64. package/subagents/src/manager.ts +28 -10
  65. package/subagents/src/navigator.ts +336 -180
  66. package/subagents/src/view.ts +97 -28
  67. package/trajectory/src/assets/index.html +347 -231
  68. package/trajectory/src/assets/prism.min.js +13 -0
  69. package/trajectory/src/export.ts +4 -2
  70. package/trajectory/src/index.ts +118 -38
  71. package/trajectory/src/server.ts +112 -12
@@ -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 getAgentDir3 } from "@earendil-works/pi-coding-agent";
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");
@@ -260,6 +260,12 @@ function resourcePatternHasMagic(pattern) {
260
260
  function loadLaunchSnapshot(input) {
261
261
  return deepFreeze(structuredClone(input));
262
262
  }
263
+ function validateSchema(schema, at = "schema") {
264
+ if (!object(schema) || Object.getPrototypeOf(schema) !== Object.prototype || !jsonValue(schema)) fail("INVALID_SCHEMA", `${at} must be a plain JSON-compatible Schema object`);
265
+ 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`);
266
+ 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`);
267
+ if (schema.properties !== void 0 && !object(schema.properties)) fail("INVALID_SCHEMA", `${at}.properties must be an object`);
268
+ }
263
269
 
264
270
  // src/decoders.ts
265
271
  var INVALID_PERSISTED_VALUE = /* @__PURE__ */ Symbol("invalid persisted value");
@@ -394,7 +400,7 @@ function decodeWorkflowExtensions(value) {
394
400
  }
395
401
  if (namespace === "trajectory") {
396
402
  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 }, ...raw.themes === void 0 ? {} : { themes: raw.themes } };
403
+ normalized.trajectory = { ...raw.port === void 0 ? {} : { port: raw.port } };
398
404
  continue;
399
405
  }
400
406
  if (!jsonValue(raw)) return void 0;
@@ -1012,8 +1018,8 @@ async function listRunIds(cwd, sessionId, home = homedir2(), cleanTemps = true)
1012
1018
  // src/trajectory.ts
1013
1019
  import { open as open2, readFile as readFile4, readdir as readdir3, stat as stat3 } from "node:fs/promises";
1014
1020
  import { homedir as homedir5 } from "node:os";
1015
- import { basename as basename4, dirname as dirname4, join as join6, resolve as resolve4 } from "node:path";
1016
- import { createBashToolDefinition, createEditToolDefinition, createFindToolDefinition, createGrepToolDefinition, createLsToolDefinition, createReadToolDefinition, createWriteToolDefinition, DefaultPackageManager, getAgentDir as getAgentDir2, SettingsManager, DEFAULT_MAX_BYTES } from "@earendil-works/pi-coding-agent";
1021
+ import { basename as basename3, dirname as dirname4, join as join6, resolve as resolve4 } from "node:path";
1022
+ import { createBashToolDefinition, createEditToolDefinition, createFindToolDefinition, createGrepToolDefinition, createLsToolDefinition, createReadToolDefinition, createWriteToolDefinition, DefaultPackageManager, getAgentDir as getAgentDir3, SettingsManager, DEFAULT_MAX_BYTES } from "@earendil-works/pi-coding-agent";
1017
1023
 
1018
1024
  // src/store.ts
1019
1025
  import { execFile as execFile2 } from "node:child_process";
@@ -1878,7 +1884,7 @@ var RunStore = class _RunStore {
1878
1884
 
1879
1885
  // src/host-view.ts
1880
1886
  import { keyHint, truncateToVisualLines } from "@earendil-works/pi-coding-agent";
1881
- import { truncateToWidth as truncateToWidth2 } from "@earendil-works/pi-tui";
1887
+ import { truncateToWidth as truncateToWidth2, visibleWidth as visibleWidth2 } from "@earendil-works/pi-tui";
1882
1888
 
1883
1889
  // src/background-widget.ts
1884
1890
  import { isKeyRelease, Key, matchesKey, truncateToWidth, visibleWidth } from "@earendil-works/pi-tui";
@@ -1892,19 +1898,208 @@ var ANSI_SGR = new RegExp(ANSI_SGR_SOURCE);
1892
1898
  import { Type } from "@earendil-works/pi-ai";
1893
1899
 
1894
1900
  // 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
1901
  import * as acorn from "acorn";
1900
- import { getAgentDir, parseFrontmatter } from "@earendil-works/pi-coding-agent";
1902
+
1903
+ // src/roles.ts
1904
+ import { getAgentDir as getAgentDir2, parseFrontmatter } from "@earendil-works/pi-coding-agent";
1901
1905
 
1902
1906
  // src/registry.ts
1903
- import { readFileSync } from "node:fs";
1904
- import { basename as basename2, dirname as dirname2, isAbsolute, join as join4 } from "node:path";
1905
- import { fileURLToPath } from "node:url";
1907
+ import { readFileSync as readFileSync2 } from "node:fs";
1908
+ import { basename as basename2, dirname as dirname3, isAbsolute, join as join5 } from "node:path";
1909
+ import { fileURLToPath as fileURLToPath2 } from "node:url";
1906
1910
  import { Value } from "typebox/value";
1907
1911
 
1912
+ // src/settings.ts
1913
+ import { mkdirSync, readFileSync } from "node:fs";
1914
+ import { homedir as homedir4 } from "node:os";
1915
+ import { dirname as dirname2, join as join4, resolve as resolve3 } from "node:path";
1916
+ import { fileURLToPath } from "node:url";
1917
+ import { getAgentDir } from "@earendil-works/pi-coding-agent";
1918
+ var ROLE_DIRECTORY = "pi-extensible-workflows";
1919
+ var DEFAULT_SETTINGS = Object.freeze({ concurrency: 8, backgroundWidget: true });
1920
+ function workflowSettingsPath(agentDir = getAgentDir()) {
1921
+ return join4(agentDir, ROLE_DIRECTORY, "settings.json");
1922
+ }
1923
+ function normalizedResourcePath(value, settingsPath) {
1924
+ if (value === "*") return value;
1925
+ let expanded = value === "~" ? homedir4() : value.startsWith("~/") || value.startsWith("~\\") ? join4(homedir4(), value.slice(2)) : value;
1926
+ if (expanded.startsWith("file://")) expanded = fileURLToPath(expanded);
1927
+ const resolved = resolve3(dirname2(settingsPath), expanded);
1928
+ if (expanded === "**" || expanded.startsWith("**/") || expanded.startsWith("**\\")) return expanded;
1929
+ if (resourcePatternHasMagic(expanded)) {
1930
+ const magicIndex = resolved.search(/[*?\x5b\x5d{}()]/);
1931
+ const separatorIndex = Math.max(resolved.lastIndexOf("/", magicIndex), resolved.lastIndexOf("\\", magicIndex));
1932
+ const rootBoundary = separatorIndex === 0 || separatorIndex === 2 && /^[A-Za-z]:[\\/]/.test(resolved);
1933
+ const prefix = rootBoundary ? resolved.slice(0, separatorIndex + 1) : separatorIndex >= 0 ? resolved.slice(0, separatorIndex) : resolved;
1934
+ const suffix = rootBoundary ? resolved.slice(separatorIndex + 1) : separatorIndex >= 0 ? resolved.slice(separatorIndex) : "";
1935
+ return `${canonicalPath(prefix)}${suffix}`;
1936
+ }
1937
+ return canonicalPath(resolved);
1938
+ }
1939
+ function validateSelectorList(value, path, kind, errorCode2 = "INVALID_SETTINGS", normalizeExtensions = true) {
1940
+ if (value === void 0) return void 0;
1941
+ if (!Array.isArray(value)) fail(errorCode2, `${path}.${kind} must be an array`);
1942
+ const normalized = [];
1943
+ for (const [index, entry] of value.entries()) {
1944
+ if (typeof entry !== "string" || !entry.trim()) fail(errorCode2, `${path}.${kind}[${String(index)}] must be a non-empty string`);
1945
+ let selector = entry.trim();
1946
+ if (kind === "extensions" && normalizeExtensions) {
1947
+ const negated = selector.startsWith("!");
1948
+ const body = negated ? selector.slice(1) : selector;
1949
+ if (!body) fail(errorCode2, `${path}.${kind}[${String(index)}] must be a valid minimatch pattern: Empty minimatch pattern ${JSON.stringify(selector)}`);
1950
+ try {
1951
+ selector = `${negated ? "!" : ""}${normalizedResourcePath(body, path)}`;
1952
+ } catch (error) {
1953
+ fail(errorCode2, `${path}.${kind}[${String(index)}] must be a valid path: ${errorText(error)}`);
1954
+ }
1955
+ }
1956
+ try {
1957
+ validateResourcePattern(selector);
1958
+ } catch (error) {
1959
+ fail(errorCode2, `${path}.${kind}[${String(index)}] must be a valid minimatch pattern: ${errorText(error)}`);
1960
+ }
1961
+ normalized.push(selector);
1962
+ }
1963
+ return Object.freeze(normalized);
1964
+ }
1965
+ function validateWorktreePostCreateCommand(value, settingsPath) {
1966
+ if (value === void 0) return void 0;
1967
+ if (!Array.isArray(value) || value.length === 0) fail("INVALID_SETTINGS", `${settingsPath}.worktreePostCreateCommand must be a non-empty array`);
1968
+ return Object.freeze(value.map((entry, index) => {
1969
+ if (typeof entry !== "string" || !entry.trim()) fail("INVALID_SETTINGS", `${settingsPath}.worktreePostCreateCommand[${String(index)}] must be a non-empty string`);
1970
+ return entry;
1971
+ }));
1972
+ }
1973
+ function selectorsFromSettings(settings) {
1974
+ return {
1975
+ ...settings.skills === void 0 ? {} : { skills: settings.skills },
1976
+ ...settings.extensions === void 0 ? {} : { extensions: settings.extensions },
1977
+ ...settings.tools === void 0 ? {} : { tools: settings.tools }
1978
+ };
1979
+ }
1980
+ function selectorSet(value) {
1981
+ return { skills: [...value?.skills ?? []], extensions: [...value?.extensions ?? []], ...value?.tools === void 0 ? {} : { tools: [...value.tools] } };
1982
+ }
1983
+ function validateWorkflowExtensionSettings(value, settingsPath, errorCode2 = "INVALID_SETTINGS") {
1984
+ if (value === void 0) return void 0;
1985
+ const base = `${settingsPath}.extensionSettings`;
1986
+ if (!object(value)) fail(errorCode2, `${base} must be an object`);
1987
+ const normalized = {};
1988
+ for (const [namespace, raw] of Object.entries(value)) {
1989
+ if (!validWorkflowExtensionNamespace(namespace)) fail(errorCode2, `${base} contains an invalid namespace: ${namespace}`);
1990
+ if (namespace === "herdr") {
1991
+ if (!object(raw)) fail(errorCode2, `${base}.herdr must be an object`);
1992
+ if (Object.keys(raw).some((key) => key !== "enableFullyInspectableMode")) fail(errorCode2, `${base}.herdr contains an unsupported setting`);
1993
+ if (raw.enableFullyInspectableMode !== void 0 && typeof raw.enableFullyInspectableMode !== "boolean") fail(errorCode2, `${base}.herdr.enableFullyInspectableMode must be a boolean`);
1994
+ normalized.herdr = Object.freeze({ ...raw.enableFullyInspectableMode === void 0 ? {} : { enableFullyInspectableMode: raw.enableFullyInspectableMode } });
1995
+ continue;
1996
+ }
1997
+ if (namespace === "trajectory") {
1998
+ if (!object(raw)) fail(errorCode2, `${base}.trajectory must be an object`);
1999
+ if (Object.keys(raw).some((key) => key !== "port")) fail(errorCode2, `${base}.trajectory contains an unsupported setting`);
2000
+ if (raw.port !== void 0 && (!positiveInteger(raw.port) || raw.port > 65535)) fail(errorCode2, `${base}.trajectory.port must be an integer from 1 to 65535`);
2001
+ normalized.trajectory = Object.freeze({ ...raw.port === void 0 ? {} : { port: raw.port } });
2002
+ continue;
2003
+ }
2004
+ if (!jsonValue(raw)) fail(errorCode2, `${base}.${namespace} must be JSON-compatible`);
2005
+ normalized[namespace] = structuredClone(raw);
2006
+ }
2007
+ return deepFreeze(normalized);
2008
+ }
2009
+ function positiveRetentionInteger(value) {
2010
+ return positiveInteger(value) && Number.isSafeInteger(value);
2011
+ }
2012
+ function validateRetention(value, settingsPath) {
2013
+ if (value === void 0) return void 0;
2014
+ const base = `${settingsPath}.retention`;
2015
+ if (!object(value)) fail("INVALID_SETTINGS", `${base} must be an object`);
2016
+ const unknown = Object.keys(value).find((key) => key !== "olderThanDays" && key !== "maxTerminalRuns");
2017
+ if (unknown) fail("INVALID_SETTINGS", `Unknown retention setting at ${base}: ${unknown}`);
2018
+ if (value.olderThanDays !== void 0 && !positiveRetentionInteger(value.olderThanDays)) fail("INVALID_SETTINGS", `${base}.olderThanDays must be a positive integer`);
2019
+ if (value.maxTerminalRuns !== void 0 && !positiveRetentionInteger(value.maxTerminalRuns)) fail("INVALID_SETTINGS", `${base}.maxTerminalRuns must be a positive integer`);
2020
+ return Object.freeze({ ...value.olderThanDays === void 0 ? {} : { olderThanDays: value.olderThanDays }, ...value.maxTerminalRuns === void 0 ? {} : { maxTerminalRuns: value.maxTerminalRuns } });
2021
+ }
2022
+ function parseSettings(path, partial) {
2023
+ let parsed;
2024
+ try {
2025
+ parsed = JSON.parse(readFileSync(path, "utf8"));
2026
+ } catch (error) {
2027
+ if (isNodeError(error, "ENOENT")) return partial ? Object.freeze({}) : DEFAULT_SETTINGS;
2028
+ fail("CONFIG_ERROR", `Invalid workflow settings JSON at ${path}: ${errorText(error)}`);
2029
+ }
2030
+ if (!object(parsed)) fail("INVALID_SETTINGS", `Workflow settings at ${path} must be an object`);
2031
+ const allowed = /* @__PURE__ */ new Set(["concurrency", "modelAliases", "skills", "extensions", "extensionSettings", "tools", "retention", "worktreePostCreateCommand", ...partial ? [] : ["backgroundWidget"]]);
2032
+ const unknown = Object.keys(parsed).find((key) => !allowed.has(key));
2033
+ if (Object.prototype.hasOwnProperty.call(parsed, "disabledAgentResources")) fail("INVALID_SETTINGS", `disabledAgentResources is no longer supported; use skills, extensions, and tools selectors (settings: ${path})`);
2034
+ if (unknown) fail("INVALID_SETTINGS", `Unknown workflow setting at ${path}: ${unknown}`);
2035
+ const concurrency = parsed.concurrency === void 0 ? partial ? void 0 : DEFAULT_SETTINGS.concurrency : parsed.concurrency;
2036
+ if (concurrency !== void 0 && (!positiveInteger(concurrency) || concurrency > 16)) fail("INVALID_SETTINGS", `${path}.concurrency must be an integer from 1 to 16`);
2037
+ const backgroundWidget = parsed.backgroundWidget === void 0 ? partial ? void 0 : DEFAULT_SETTINGS.backgroundWidget : parsed.backgroundWidget;
2038
+ if (backgroundWidget !== void 0 && typeof backgroundWidget !== "boolean") fail("INVALID_SETTINGS", `${path}.backgroundWidget must be a boolean`);
2039
+ const modelAliases = parsed.modelAliases === void 0 ? void 0 : validateModelAliases(parsed.modelAliases, path);
2040
+ const skills = validateSelectorList(parsed.skills, path, "skills");
2041
+ const tools = validateSelectorList(parsed.tools, path, "tools");
2042
+ const extensions = validateSelectorList(parsed.extensions, path, "extensions");
2043
+ const extensionSettings = parsed.extensionSettings === void 0 ? void 0 : validateWorkflowExtensionSettings(parsed.extensionSettings, path, "INVALID_SETTINGS");
2044
+ const retention = validateRetention(parsed.retention, path);
2045
+ const worktreePostCreateCommand = validateWorktreePostCreateCommand(parsed.worktreePostCreateCommand, path);
2046
+ return Object.freeze({
2047
+ ...concurrency === void 0 ? {} : { concurrency },
2048
+ ...backgroundWidget === void 0 ? {} : { backgroundWidget },
2049
+ ...modelAliases === void 0 ? {} : { modelAliases },
2050
+ ...skills === void 0 ? {} : { skills },
2051
+ ...extensions === void 0 ? {} : { extensions },
2052
+ ...extensionSettings === void 0 ? {} : { extensionSettings },
2053
+ ...tools === void 0 ? {} : { tools },
2054
+ ...retention === void 0 ? {} : { retention },
2055
+ ...worktreePostCreateCommand === void 0 ? {} : { worktreePostCreateCommand }
2056
+ });
2057
+ }
2058
+ function loadSettings(path = workflowSettingsPath()) {
2059
+ return parseSettings(path, false);
2060
+ }
2061
+ function loadSettingsOverrides(path) {
2062
+ return parseSettings(path, true);
2063
+ }
2064
+ function resolveWorkflowSettings(cwd, projectTrusted, globalSettingsPath = workflowSettingsPath()) {
2065
+ const projectSettingsPath = workflowProjectSettingsPath(cwd);
2066
+ const global = loadSettings(globalSettingsPath);
2067
+ const project = projectTrusted ? loadSettingsOverrides(projectSettingsPath) : Object.freeze({});
2068
+ const projectHas = (key) => Object.prototype.hasOwnProperty.call(project, key);
2069
+ const sourceFor = (key) => projectHas(key) ? projectSettingsPath : globalSettingsPath;
2070
+ const globalSelectors = selectorsFromSettings(global);
2071
+ const projectSelectors = selectorsFromSettings(project);
2072
+ const effectiveSelectors = selectorSet({
2073
+ skills: [...globalSelectors.skills ?? [], ...projectSelectors.skills ?? []],
2074
+ extensions: [...globalSelectors.extensions ?? [], ...projectSelectors.extensions ?? []],
2075
+ ...globalSelectors.tools === void 0 && projectSelectors.tools === void 0 ? {} : { tools: [...globalSelectors.tools ?? [], ...projectSelectors.tools ?? []] }
2076
+ });
2077
+ const hasExtensionSelectors = global.extensions !== void 0 || project.extensions !== void 0;
2078
+ const extensionSettings = projectHas("extensionSettings") ? project.extensionSettings : global.extensionSettings;
2079
+ const sources = {
2080
+ concurrency: projectHas("concurrency") ? projectSettingsPath : globalSettingsPath,
2081
+ modelAliases: projectHas("modelAliases") ? projectSettingsPath : globalSettingsPath,
2082
+ skills: sourceFor("skills"),
2083
+ extensions: sourceFor("extensions"),
2084
+ tools: sourceFor("tools"),
2085
+ ...extensionSettings === void 0 ? {} : { extensionSettings: projectHas("extensionSettings") ? projectSettingsPath : globalSettingsPath },
2086
+ ...project.retention === void 0 && global.retention === void 0 ? {} : { retention: project.retention === void 0 ? globalSettingsPath : projectSettingsPath },
2087
+ ...projectHas("worktreePostCreateCommand") || global.worktreePostCreateCommand !== void 0 ? { worktreePostCreateCommand: projectHas("worktreePostCreateCommand") ? projectSettingsPath : globalSettingsPath } : {}
2088
+ };
2089
+ const effective = Object.freeze({
2090
+ concurrency: project.concurrency ?? global.concurrency,
2091
+ backgroundWidget: global.backgroundWidget ?? true,
2092
+ ...projectHas("modelAliases") ? { modelAliases: project.modelAliases } : global.modelAliases === void 0 ? {} : { modelAliases: global.modelAliases },
2093
+ ...effectiveSelectors.skills.length ? { skills: effectiveSelectors.skills } : global.skills !== void 0 || project.skills !== void 0 ? { skills: effectiveSelectors.skills } : {},
2094
+ ...hasExtensionSelectors ? { extensions: effectiveSelectors.extensions } : {},
2095
+ ...extensionSettings === void 0 ? {} : { extensionSettings },
2096
+ ...effectiveSelectors.tools?.length ? { tools: effectiveSelectors.tools } : global.tools !== void 0 || project.tools !== void 0 ? { tools: effectiveSelectors.tools } : {},
2097
+ ...(project.retention ?? global.retention) === void 0 ? {} : { retention: project.retention ?? global.retention },
2098
+ ...projectHas("worktreePostCreateCommand") ? { worktreePostCreateCommand: project.worktreePostCreateCommand } : global.worktreePostCreateCommand === void 0 ? {} : { worktreePostCreateCommand: global.worktreePostCreateCommand }
2099
+ });
2100
+ return { globalSettingsPath, projectSettingsPath, projectTrusted, global, project, effective, sources };
2101
+ }
2102
+
1908
2103
  // src/package-identity.ts
1909
2104
  var CORE_PACKAGE_NAME = "@marcoscale98/pi-extensible-workflows";
1910
2105
  var CORE_PACKAGE_COMPATIBILITY_NAME = "pi-extensible-workflows";
@@ -1922,10 +2117,10 @@ function normalizeRoleDirectory(value) {
1922
2117
  try {
1923
2118
  if (value instanceof URL) {
1924
2119
  if (value.protocol !== "file:") fail("INVALID_METADATA", "Workflow role directories require file URLs or absolute filesystem paths");
1925
- return fileURLToPath(value);
2120
+ return fileURLToPath2(value);
1926
2121
  }
1927
2122
  if (typeof value === "string") {
1928
- if (value.startsWith("file:")) return fileURLToPath(value);
2123
+ if (value.startsWith("file:")) return fileURLToPath2(value);
1929
2124
  if (isAbsolute(value)) return value;
1930
2125
  }
1931
2126
  } catch (error) {
@@ -1935,12 +2130,12 @@ function normalizeRoleDirectory(value) {
1935
2130
  }
1936
2131
  function isBuiltinRoleDirectory(path) {
1937
2132
  const canonical = canonicalPath(path);
1938
- const starterDirectory = dirname2(canonical);
2133
+ const starterDirectory = dirname3(canonical);
1939
2134
  if (basename2(canonical) !== "roles" || basename2(starterDirectory) !== "starter") return false;
1940
- const distributionDirectory = dirname2(starterDirectory);
1941
- const packageRoot = basename2(distributionDirectory) === "dist" ? dirname2(distributionDirectory) : distributionDirectory;
2135
+ const distributionDirectory = dirname3(starterDirectory);
2136
+ const packageRoot = basename2(distributionDirectory) === "dist" ? dirname3(distributionDirectory) : distributionDirectory;
1942
2137
  try {
1943
- const metadata = JSON.parse(readFileSync(join4(packageRoot, "package.json"), "utf8"));
2138
+ const metadata = JSON.parse(readFileSync2(join5(packageRoot, "package.json"), "utf8"));
1944
2139
  return object(metadata) && isCorePackageName(metadata.name);
1945
2140
  } catch {
1946
2141
  return false;
@@ -2214,199 +2409,6 @@ function beginWorkflowExtensionLoading() {
2214
2409
  }
2215
2410
  beginWorkflowExtensionLoading();
2216
2411
 
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
2412
  // subagents/src/contracts.ts
2411
2413
  import { Value as Value2 } from "typebox/value";
2412
2414
  var SUBAGENT_MAX_RETRIES = 255;
@@ -2605,15 +2607,15 @@ function canonicalExtensionSelector(selector, base) {
2605
2607
  return `${negated ? "!" : ""}${canonicalPath(resolved)}`;
2606
2608
  }
2607
2609
  function skillNameFromPath(path) {
2608
- const file = basename4(path);
2609
- return file.toLowerCase() === "skill.md" ? basename4(dirname4(path)) : file;
2610
+ const file = basename3(path);
2611
+ return file.toLowerCase() === "skill.md" ? basename3(dirname4(path)) : file;
2610
2612
  }
2611
2613
  var discoveredResourcesByCwd = /* @__PURE__ */ new Map();
2612
2614
  async function discoveredResources(cwd) {
2613
2615
  const cached = discoveredResourcesByCwd.get(cwd);
2614
2616
  if (cached) return cached;
2615
2617
  const pending = (async () => {
2616
- const agentDir = getAgentDir2();
2618
+ const agentDir = getAgentDir3();
2617
2619
  const settingsManager = SettingsManager.create(cwd, agentDir, { projectTrusted: false });
2618
2620
  const packageManager = new DefaultPackageManager({ cwd, agentDir, settingsManager });
2619
2621
  const resolved = await packageManager.resolve();
@@ -2660,11 +2662,46 @@ function withoutAgentActivities(run) {
2660
2662
  });
2661
2663
  return agents.every((agent, index) => agent === run.agents[index]) ? run : { ...run, agents };
2662
2664
  }
2665
+ function serializedJsonBytes(value) {
2666
+ const serialized = JSON.stringify(value);
2667
+ return Buffer.byteLength(serialized, "utf8");
2668
+ }
2669
+ function failedAgentOutput(agent) {
2670
+ const error = [...agent.attemptDetails ?? []].reverse().find((attempt) => attempt.error)?.error;
2671
+ const status = agent.state === "cancelled" ? "cancelled" : "failed";
2672
+ return { status, code: error?.code ?? (status === "cancelled" ? "CANCELLED" : "AGENT_FAILED"), message: error?.message ?? `Agent ${agent.state}` };
2673
+ }
2674
+ function outputForAgent(agent, operation) {
2675
+ if (operation !== void 0) {
2676
+ const bytes = serializedJsonBytes(operation.value);
2677
+ return bytes > DEFAULT_MAX_BYTES ? { status: "truncated", kind: "result", bytes } : { status: "available", value: operation.value, bytes };
2678
+ }
2679
+ if (agent.state === "failed" || agent.state === "cancelled") return failedAgentOutput(agent);
2680
+ if (agent.state === "completed") return { status: "unavailable" };
2681
+ return { status: "pending" };
2682
+ }
2683
+ function applyTrajectoryAgentOutputs(run, operations) {
2684
+ const byPath = new Map(operations.map((operation) => [operation.path, operation]));
2685
+ const agents = run.agents.map((agent) => ({ ...agent, output: outputForAgent(agent, agent.resultPath === void 0 ? void 0 : byPath.get(agent.resultPath)) }));
2686
+ return { ...run, agents };
2687
+ }
2688
+ function overlayTrajectoryRun(run, overlay) {
2689
+ const overlaid = overlay(run);
2690
+ const outputs = new Map(run.agents.map((agent) => [agent.id, agent.output]));
2691
+ const agents = overlaid.agents.map((agent) => {
2692
+ const previous = outputs.get(agent.id);
2693
+ const output = previous?.status === "available" || previous?.status === "truncated" ? previous : outputForAgent(agent, void 0);
2694
+ return { ...agent, output };
2695
+ });
2696
+ return { ...overlaid, agents };
2697
+ }
2663
2698
  async function loadTrajectoryRun(store, includeTranscripts = true) {
2664
2699
  const value = await store.load();
2665
2700
  const summary = await store.loadSummary().catch(() => void 0);
2666
2701
  const prompts = await store.systemPrompts().catch(() => []);
2667
- const run = withoutAgentActivities(await withResolvedResources(withPiToolDescriptions(applySystemPrompts(value.run, prompts)), store.cwd));
2702
+ const prepared = withoutAgentActivities(await withResolvedResources(withPiToolDescriptions(applySystemPrompts(value.run, prompts)), store.cwd));
2703
+ const operations = await store.replayableOperations().catch(() => []);
2704
+ const run = applyTrajectoryAgentOutputs(prepared, operations);
2668
2705
  return { run, snapshot: value.snapshot, awaiting: await store.awaitingCheckpoints(), ...summary?.createdAt === void 0 ? {} : { createdAt: summary.createdAt }, transcripts: includeTranscripts ? await runTranscripts(run) : {} };
2669
2706
  }
2670
2707
  async function loadTrajectoryRuns(cwd, sessionId, home = homedir5(), overlay) {
@@ -2672,7 +2709,7 @@ async function loadTrajectoryRuns(cwd, sessionId, home = homedir5(), overlay) {
2672
2709
  for (const runId of await listRunIds(cwd, sessionId, home, false)) {
2673
2710
  try {
2674
2711
  const value = await loadTrajectoryRun(new RunStore(cwd, sessionId, runId, home));
2675
- loaded.push(overlay ? { ...value, run: overlay(value.run) } : value);
2712
+ loaded.push(overlay ? { ...value, run: overlayTrajectoryRun(value.run, overlay) } : value);
2676
2713
  } catch {
2677
2714
  }
2678
2715
  }
@@ -2695,15 +2732,16 @@ async function exportTrajectoryRunHtml(options) {
2695
2732
  const run = runs.find((candidate) => candidate.run.id === options.runId);
2696
2733
  if (!run) throw new Error(`Workflow run ${options.runId} was not found for session ${options.sessionId}`);
2697
2734
  const assets = new URL("./assets/", import.meta.url);
2698
- const [html, marked, morphdom, favicon] = await Promise.all([
2735
+ const [html, marked, morphdom, prism, favicon] = await Promise.all([
2699
2736
  readFile5(new URL("index.html", assets), "utf8"),
2700
2737
  readFile5(new URL("marked.min.js", assets)),
2701
2738
  readFile5(new URL("morphdom.min.js", assets)),
2739
+ readFile5(new URL("prism.min.js", assets)),
2702
2740
  readFile5(new URL("favicon.png", assets))
2703
2741
  ]);
2704
2742
  const state = {
2705
2743
  type: "state",
2706
- publishers: [{ id: `export-${options.runId}`, title: `run ${options.runId.slice(0, 8)}`, cwd: options.cwd, sessionId: options.sessionId, themes: true, connected: true, runs: [run], subagents: [] }],
2744
+ publishers: [{ id: `export-${options.runId}`, title: `run ${options.runId.slice(0, 8)}`, cwd: options.cwd, sessionId: options.sessionId, connected: true, runs: [run], subagents: [] }],
2707
2745
  updatedAt: Date.now()
2708
2746
  };
2709
2747
  const script = (source) => `<script src="data:text/javascript;base64,${source.toString("base64")}"></script>`;
@@ -2711,6 +2749,7 @@ async function exportTrajectoryRunHtml(options) {
2711
2749
  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
2750
  output = inlineAsset(output, '<script src="./marked.min.js"></script>', script(marked));
2713
2751
  output = inlineAsset(output, '<script src="./morphdom.min.js"></script>', script(morphdom));
2752
+ output = inlineAsset(output, '<script src="./prism.min.js"></script>', script(prism));
2714
2753
  output = inlineAsset(output, '<body data-view="run">', `<body data-view="run">
2715
2754
  <script>window.__PIEWF_STATIC__ = ${JSON.stringify(state).replace(/</g, "\\u003c")};</script>`);
2716
2755
  return output;
@@ -2771,14 +2810,20 @@ function trajectoryPort(value) {
2771
2810
  function delay(ms) {
2772
2811
  return new Promise((resolve5) => setTimeout(resolve5, ms));
2773
2812
  }
2774
- async function serverHealthy(port) {
2813
+ async function serverHealth(port) {
2775
2814
  try {
2776
2815
  const response = await fetch(`http://127.0.0.1:${String(port)}/health`, { signal: AbortSignal.timeout(300) });
2777
- return response.ok;
2816
+ if (!response.ok) return void 0;
2817
+ const body = await response.json().catch(() => void 0);
2818
+ if (!object(body)) return {};
2819
+ return { ...positiveInteger(body.pid) ? { pid: body.pid } : {}, ...typeof body.fingerprint === "string" ? { fingerprint: body.fingerprint } : {}, ...positiveInteger(body.startedAt) ? { startedAt: body.startedAt } : {} };
2778
2820
  } catch {
2779
- return false;
2821
+ return void 0;
2780
2822
  }
2781
2823
  }
2824
+ async function serverHealthy(port) {
2825
+ return await serverHealth(port) !== void 0;
2826
+ }
2782
2827
  function signalProcess(pid, signal) {
2783
2828
  try {
2784
2829
  process.kill(pid, signal);
@@ -2811,23 +2856,27 @@ async function readLock(path) {
2811
2856
  return void 0;
2812
2857
  }
2813
2858
  }
2814
- async function waitForServer(port) {
2859
+ async function waitForServer(port, fingerprint) {
2860
+ let foreign = false;
2815
2861
  for (let attempt = 0; attempt < 60; attempt += 1) {
2816
- if (await serverHealthy(port)) return;
2862
+ const health = await serverHealth(port);
2863
+ if (health?.fingerprint === fingerprint) return;
2864
+ foreign = health !== void 0;
2817
2865
  await delay(50);
2818
2866
  }
2819
- throw new Error(`Trajectory server did not start on port ${String(port)}`);
2867
+ 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
2868
  }
2821
2869
  async function resolveExistingServer(lockPath, existing, fingerprint) {
2822
- if (await serverHealthy(existing.port)) {
2823
- if (existing.fingerprint === fingerprint) return existing;
2870
+ const health = await serverHealth(existing.port);
2871
+ if (health) {
2872
+ if (existing.fingerprint === fingerprint && health.fingerprint === fingerprint) return existing;
2824
2873
  await stopStaleServer(existing);
2825
2874
  await rm5(lockPath, { force: true });
2826
2875
  return void 0;
2827
2876
  }
2828
2877
  if (await processAlive(existing.pid, existing.startedAt)) {
2829
2878
  try {
2830
- await waitForServer(existing.port);
2879
+ await waitForServer(existing.port, fingerprint);
2831
2880
  return existing;
2832
2881
  } catch {
2833
2882
  await stopStaleServer(existing);
@@ -2870,13 +2919,15 @@ async function ensureTrajectoryServer(agentDir, configuredPort) {
2870
2919
  } finally {
2871
2920
  await lockHandle?.close();
2872
2921
  }
2922
+ const occupant = await serverHealth(configuredPort);
2923
+ if (occupant?.pid !== void 0 && occupant.fingerprint !== fingerprint) await stopStaleServer({ pid: occupant.pid, port: configuredPort, ...occupant.startedAt === void 0 ? {} : { startedAt: occupant.startedAt } });
2873
2924
  try {
2874
2925
  const child = spawn(process.versions.bun ? "node" : process.execPath, [serverPath, "--port", String(configuredPort), "--lock", lockPath, "--fingerprint", fingerprint], { detached: true, stdio: "ignore" });
2875
2926
  const startupError = new Promise((_resolve, reject) => {
2876
2927
  child.once("error", reject);
2877
2928
  });
2878
2929
  child.unref();
2879
- await Promise.race([waitForServer(configuredPort), startupError]);
2930
+ await Promise.race([waitForServer(configuredPort, fingerprint), startupError]);
2880
2931
  return { port: configuredPort };
2881
2932
  } catch (error) {
2882
2933
  await rm5(lockPath, { force: true });
@@ -2907,6 +2958,8 @@ var MAX_FRAME_BYTES = 32 * 1024 * 1024;
2907
2958
  var MAX_LIVE_STATE_BYTES = MAX_FRAME_BYTES - 1024;
2908
2959
  var MAX_TRANSCRIPT_REQUESTS = 64;
2909
2960
  var TRANSCRIPT_REQUEST_TIMEOUT_MS = 1e4;
2961
+ var ACTIVE_POLL_MS = 1e3;
2962
+ var IDLE_POLL_MS = 1e4;
2910
2963
  var RECONNECT_INITIAL_DELAY_MS = 100;
2911
2964
  var RECONNECT_MAX_DELAY_MS = 5e3;
2912
2965
  function isTimingEntry(value) {
@@ -2923,44 +2976,89 @@ var MAX_LIVE_OBJECT_KEYS = 64;
2923
2976
  var LIVE_METADATA_ARRAY_KEYS = /* @__PURE__ */ new Set(["agents", "runs", "subagents"]);
2924
2977
  var LIVE_TRUNCATABLE_ARRAY_KEYS = /* @__PURE__ */ new Set(["events", "phaseHistory"]);
2925
2978
  var LIVE_METADATA_OBJECT_KEYS = /* @__PURE__ */ new Set(["transcripts"]);
2979
+ function liveValueWillBeBounded(value, key = "", depth = 0) {
2980
+ if (typeof value === "string") return Buffer.byteLength(value) > MAX_LIVE_STRING_BYTES;
2981
+ if (typeof value !== "object" || value === null) return false;
2982
+ if (depth >= 12) return true;
2983
+ if (Array.isArray(value)) {
2984
+ const maxEntries = LIVE_TRUNCATABLE_ARRAY_KEYS.has(key) ? MAX_LIVE_ARRAY_ENTRIES - 1 : MAX_LIVE_ARRAY_ENTRIES;
2985
+ if (key === "attemptDetails" && value.length > 8 || !LIVE_METADATA_ARRAY_KEYS.has(key) && value.length > maxEntries) return true;
2986
+ return value.some((entry) => liveValueWillBeBounded(entry, "", depth + 1));
2987
+ }
2988
+ if (!object(value)) return false;
2989
+ const properties = LIVE_METADATA_OBJECT_KEYS.has(key) ? Object.keys(value) : Object.keys(value).sort().slice(0, MAX_LIVE_OBJECT_KEYS);
2990
+ if (!LIVE_METADATA_OBJECT_KEYS.has(key) && Object.keys(value).length > MAX_LIVE_OBJECT_KEYS) return true;
2991
+ return properties.some((property) => liveValueWillBeBounded(value[property], property, depth + 1));
2992
+ }
2993
+ var MAX_LIVE_TIMING_BYTES = 64 * 1024;
2926
2994
  function boundedTiming(value) {
2927
2995
  const entries = Array.isArray(value) ? value.filter(isTimingEntry) : [];
2928
2996
  const retained = [];
2929
2997
  let bytes = 2;
2930
- for (const entry of entries) {
2998
+ for (let index = entries.length - 1; index >= 0; index -= 1) {
2931
2999
  let serialized;
2932
3000
  try {
2933
- serialized = JSON.stringify(entry);
3001
+ serialized = JSON.stringify(entries[index]);
2934
3002
  } catch {
2935
3003
  continue;
2936
3004
  }
2937
3005
  const nextBytes = bytes + (retained.length ? 1 : 0) + Buffer.byteLength(serialized);
2938
- if (nextBytes >= 64 * 1024) break;
2939
- retained.push(entry);
3006
+ if (nextBytes >= MAX_LIVE_TIMING_BYTES) break;
3007
+ retained.push(entries[index]);
2940
3008
  bytes = nextBytes;
2941
3009
  }
2942
- return retained;
3010
+ return retained.reverse();
2943
3011
  }
2944
- function boundedLiveValue(value, key = "", depth = 0) {
2945
- if (typeof value === "string") return boundedString(value, MAX_LIVE_STRING_BYTES);
2946
- if (typeof value === "number" || typeof value === "boolean" || value === null) return value;
2947
- if (depth >= 12) return void 0;
2948
- if (key === "timing") return boundedTiming(value);
2949
- if (Array.isArray(value)) {
2950
- const array = value;
2951
- const maxEntries = LIVE_TRUNCATABLE_ARRAY_KEYS.has(key) ? MAX_LIVE_ARRAY_ENTRIES - 1 : MAX_LIVE_ARRAY_ENTRIES;
2952
- const entries = key === "attemptDetails" ? array.slice(-8) : LIVE_METADATA_ARRAY_KEYS.has(key) ? array : array.length <= maxEntries ? array : [...array.slice(0, 32), ...array.slice(-maxEntries + 32)];
2953
- const bounded = entries.map((entry) => boundedLiveValue(entry, "", depth + 1));
2954
- if (entries.length !== array.length && LIVE_TRUNCATABLE_ARRAY_KEYS.has(key)) bounded.push({ type: "trajectory:truncated", field: key, omitted: array.length - entries.length });
2955
- return bounded;
2956
- }
2957
- if (object(value)) {
2958
- const result = {};
2959
- const properties = LIVE_METADATA_OBJECT_KEYS.has(key) ? Object.keys(value).sort() : Object.keys(value).sort().slice(0, MAX_LIVE_OBJECT_KEYS);
2960
- for (const property of properties) result[property] = boundedLiveValue(value[property], property, depth + 1);
2961
- return result;
3012
+ function boundedLiveValue(value, key = "", depth = 0, bounds) {
3013
+ const pathLength = bounds?.path.length ?? 0;
3014
+ if (bounds && key) bounds.path.push(key);
3015
+ const argsValue = bounds !== void 0 && bounds.path.length >= 3 && bounds.path[0] === "runs" && bounds.path[1] === "snapshot" && bounds.path[2] === "args";
3016
+ const markArgsTruncated = () => {
3017
+ if (argsValue && bounds.runIndex !== void 0) bounds.argsTruncatedRuns.add(bounds.runIndex);
3018
+ };
3019
+ try {
3020
+ if (typeof value === "string") {
3021
+ if (argsValue && Buffer.byteLength(value) > MAX_LIVE_STRING_BYTES) markArgsTruncated();
3022
+ return boundedString(value, MAX_LIVE_STRING_BYTES);
3023
+ }
3024
+ if (typeof value === "number" || typeof value === "boolean" || value === null) return value;
3025
+ if (depth >= 12) {
3026
+ markArgsTruncated();
3027
+ return void 0;
3028
+ }
3029
+ if (key === "timing" && !argsValue) return boundedTiming(value);
3030
+ if (key === "output" && object(value) && value.status === "available") {
3031
+ const boundedOutput = boundedLiveValue(value.value, "value", depth + 1, bounds);
3032
+ return liveValueWillBeBounded(value.value, "value", depth + 1) ? { status: "truncated", kind: "result", bytes: value.bytes } : { ...value, value: boundedOutput };
3033
+ }
3034
+ if (Array.isArray(value)) {
3035
+ const array = value;
3036
+ const maxEntries = !argsValue && LIVE_TRUNCATABLE_ARRAY_KEYS.has(key) ? MAX_LIVE_ARRAY_ENTRIES - 1 : MAX_LIVE_ARRAY_ENTRIES;
3037
+ 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)];
3038
+ if (entries.length !== array.length) markArgsTruncated();
3039
+ const publisherRuns = bounds?.path.length === 1 && bounds.path[0] === "runs";
3040
+ const bounded = entries.map((entry, index) => {
3041
+ const previousRunIndex = bounds?.runIndex;
3042
+ if (publisherRuns) bounds.runIndex = index;
3043
+ const result = boundedLiveValue(entry, "", depth + 1, bounds);
3044
+ if (bounds) bounds.runIndex = previousRunIndex;
3045
+ return result;
3046
+ });
3047
+ if (entries.length !== array.length && LIVE_TRUNCATABLE_ARRAY_KEYS.has(key)) bounded.push({ type: "trajectory:truncated", field: key, omitted: array.length - entries.length });
3048
+ return bounded;
3049
+ }
3050
+ if (object(value)) {
3051
+ const result = {};
3052
+ const keys = Object.keys(value).sort();
3053
+ const properties = !argsValue && LIVE_METADATA_OBJECT_KEYS.has(key) ? keys : keys.slice(0, MAX_LIVE_OBJECT_KEYS);
3054
+ if (properties.length !== keys.length) markArgsTruncated();
3055
+ for (const property of properties) result[property] = boundedLiveValue(value[property], property, depth + 1, bounds);
3056
+ return result;
3057
+ }
3058
+ return void 0;
3059
+ } finally {
3060
+ if (bounds) bounds.path.length = pathLength;
2962
3061
  }
2963
- return void 0;
2964
3062
  }
2965
3063
  function transcriptRevisionProjection(value, depth = 0) {
2966
3064
  if (depth >= 8) return typeof value === "string" ? boundedString(value, 1024) : typeof value === "number" || typeof value === "boolean" || value === null ? value : void 0;
@@ -3007,7 +3105,19 @@ function projectPublisher(metadata, publisher, revisions) {
3007
3105
  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
3106
  }
3009
3107
  function minimalStatePublisher(publisher) {
3010
- return boundedLiveValue(publisher) ?? {};
3108
+ const bounds = { argsTruncatedRuns: /* @__PURE__ */ new Set(), path: [], runIndex: void 0 };
3109
+ const bounded = boundedLiveValue(publisher, "", 0, bounds) ?? {};
3110
+ const sourceRuns = Array.isArray(publisher.runs) ? publisher.runs : [];
3111
+ const boundedRuns = Array.isArray(bounded.runs) ? bounded.runs : [];
3112
+ if (!sourceRuns.length || !boundedRuns.length) return bounded;
3113
+ const runs = boundedRuns.map((run, index) => {
3114
+ const sourceRun = sourceRuns[index];
3115
+ const sourceSnapshot = object(sourceRun) && object(sourceRun.snapshot) ? sourceRun.snapshot : void 0;
3116
+ const boundedSnapshot = object(run) && object(run.snapshot) ? run.snapshot : void 0;
3117
+ const argsUnavailable = sourceSnapshot !== void 0 && sourceSnapshot.args !== void 0 && sourceSnapshot.args !== null && (boundedSnapshot === void 0 || !Object.prototype.hasOwnProperty.call(boundedSnapshot, "args"));
3118
+ return (bounds.argsTruncatedRuns.has(index) || argsUnavailable) && object(run) ? { ...run, snapshotArgsTruncated: true } : run;
3119
+ });
3120
+ return { ...bounded, runs };
3011
3121
  }
3012
3122
  function publisherStateFrame(publisher, runs, subagents, truncated = false) {
3013
3123
  const publisherSummary = { ...publisher };
@@ -3077,18 +3187,19 @@ function createTrajectoryController(agentDir) {
3077
3187
  for (const request of pendingTranscripts.values()) clearTimeout(request.timer);
3078
3188
  pendingTranscripts.clear();
3079
3189
  };
3190
+ let viewers = 0;
3080
3191
  const startPolling = () => {
3081
3192
  stopPolling();
3082
3193
  pollTimer = setInterval(() => {
3083
3194
  void sendState().catch((error) => {
3084
3195
  if (!closing) console.error(`Trajectory state publish failed: ${errorText(error)}`);
3085
3196
  });
3086
- }, 1e3);
3197
+ }, viewers > 0 ? ACTIVE_POLL_MS : IDLE_POLL_MS);
3087
3198
  pollTimer.unref();
3088
3199
  };
3089
3200
  let stateLoad;
3090
3201
  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, themes: input.themes, connected: true });
3202
+ 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
3203
  const sendState = async () => {
3093
3204
  const activeSocket = socket;
3094
3205
  if (activeSocket !== void 0 && stateLoad?.socket === activeSocket) return stateLoad.task;
@@ -3244,6 +3355,14 @@ function createTrajectoryController(agentDir) {
3244
3355
  established = false;
3245
3356
  return;
3246
3357
  }
3358
+ if (message.type === "publisher:viewers" && typeof message.count === "number") {
3359
+ const watched = viewers > 0;
3360
+ viewers = message.count;
3361
+ if (watched === viewers > 0) return;
3362
+ if (socket === next && !closing) startPolling();
3363
+ if (viewers > 0) void sendState().catch(() => void 0);
3364
+ return;
3365
+ }
3247
3366
  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
3367
  handleTranscriptRequest(next, generation, message, input);
3249
3368
  return;
@@ -3398,7 +3517,7 @@ function createTrajectoryController(agentDir) {
3398
3517
  };
3399
3518
  }
3400
3519
  function registerTrajectoryExtension(pi, options = {}) {
3401
- const controller = options.controller ?? createTrajectoryController(options.agentDir ?? getAgentDir3());
3520
+ const controller = options.controller ?? createTrajectoryController(options.agentDir ?? getAgentDir4());
3402
3521
  const openUrl = options.openUrl ?? openTrajectoryUrl;
3403
3522
  let autoAttached = false;
3404
3523
  const notify = (context, message, level) => {
@@ -3450,6 +3569,7 @@ export {
3450
3569
  createTrajectoryController,
3451
3570
  extension as default,
3452
3571
  exportTrajectoryRunHtml,
3572
+ minimalStatePublisher,
3453
3573
  openTrajectoryUrl,
3454
3574
  registerTrajectoryExtension,
3455
3575
  shareTrajectoryRun,