@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.
Files changed (94) hide show
  1. package/CHANGELOG.md +135 -0
  2. package/dist/src/agent-execution.js +114 -87
  3. package/dist/src/background-widget.d.ts +61 -0
  4. package/dist/src/background-widget.js +23 -16
  5. package/dist/src/decoders.js +28 -1
  6. package/dist/src/execution.d.ts +4 -2
  7. package/dist/src/execution.js +43 -2
  8. package/dist/src/host-delivery.js +1 -0
  9. package/dist/src/host-navigator.d.ts +18 -0
  10. package/dist/src/host-navigator.js +71 -13
  11. package/dist/src/host-recovery.d.ts +5 -2
  12. package/dist/src/host-recovery.js +3 -3
  13. package/dist/src/host-view.d.ts +21 -1
  14. package/dist/src/host-view.js +138 -47
  15. package/dist/src/host.d.ts +2 -2
  16. package/dist/src/host.js +246 -60
  17. package/dist/src/index.d.ts +3 -1
  18. package/dist/src/index.js +1346 -622
  19. package/dist/src/index.js.map +3 -3
  20. package/dist/src/paths.d.ts +2 -0
  21. package/dist/src/paths.js +2 -0
  22. package/dist/src/registry.js +3 -2
  23. package/dist/src/roles.d.ts +70 -0
  24. package/dist/src/roles.js +326 -0
  25. package/dist/src/script-tools.d.ts +27 -0
  26. package/dist/src/script-tools.js +87 -0
  27. package/dist/src/settings.d.ts +26 -0
  28. package/dist/src/settings.js +277 -0
  29. package/dist/src/trajectory.d.ts +39 -4
  30. package/dist/src/trajectory.js +82 -5
  31. package/dist/src/types.d.ts +30 -3
  32. package/dist/src/types.js +1 -1
  33. package/dist/src/utils.d.ts +21 -1
  34. package/dist/src/utils.js +40 -0
  35. package/dist/src/validation.d.ts +10 -17
  36. package/dist/src/validation.js +29 -458
  37. package/dist/starter/index.js +0 -2
  38. package/dist/starter/index.js.map +1 -1
  39. package/dist/starter/review-loop.d.ts +0 -13
  40. package/dist/starter/review-loop.js +0 -2
  41. package/dist/subagents/index.js +1344 -798
  42. package/dist/subagents/index.js.map +3 -3
  43. package/dist/subagents/src/contracts.d.ts +49 -29
  44. package/dist/subagents/src/contracts.js +18 -7
  45. package/dist/subagents/src/index.d.ts +1 -1
  46. package/dist/subagents/src/index.js +15 -7
  47. package/dist/subagents/src/manager.js +32 -13
  48. package/dist/subagents/src/navigator.js +396 -247
  49. package/dist/subagents/src/view.d.ts +24 -2
  50. package/dist/subagents/src/view.js +107 -80
  51. package/dist/trajectory/assets/index.html +370 -232
  52. package/dist/trajectory/assets/prism.min.js +13 -0
  53. package/dist/trajectory/index.js +445 -277
  54. package/dist/trajectory/index.js.map +3 -3
  55. package/dist/trajectory/src/assets/index.html +370 -232
  56. package/dist/trajectory/src/assets/prism.min.js +13 -0
  57. package/dist/trajectory/src/export.js +4 -2
  58. package/dist/trajectory/src/index.d.ts +2 -0
  59. package/dist/trajectory/src/index.js +151 -60
  60. package/dist/trajectory/src/server.js +138 -11
  61. package/dist/trajectory/src/server.js.map +2 -2
  62. package/package.json +7 -6
  63. package/skills/pi-extensible-workflows/SKILL.md +2 -0
  64. package/src/agent-execution.ts +110 -80
  65. package/src/background-widget.ts +26 -19
  66. package/src/decoders.ts +22 -1
  67. package/src/execution.ts +41 -4
  68. package/src/host-delivery.ts +1 -0
  69. package/src/host-navigator.ts +58 -14
  70. package/src/host-recovery.ts +8 -7
  71. package/src/host-view.ts +116 -42
  72. package/src/host.ts +223 -55
  73. package/src/index.ts +3 -1
  74. package/src/paths.ts +3 -0
  75. package/src/registry.ts +3 -2
  76. package/src/roles.ts +327 -0
  77. package/src/script-tools.ts +76 -0
  78. package/src/settings.ts +224 -0
  79. package/src/trajectory.ts +97 -9
  80. package/src/types.ts +12 -5
  81. package/src/utils.ts +36 -1
  82. package/src/validation.ts +30 -356
  83. package/starter/review-loop.ts +0 -2
  84. package/subagents/README.md +3 -3
  85. package/subagents/src/contracts.ts +22 -7
  86. package/subagents/src/index.ts +18 -7
  87. package/subagents/src/manager.ts +31 -13
  88. package/subagents/src/navigator.ts +336 -180
  89. package/subagents/src/view.ts +108 -76
  90. package/trajectory/src/assets/index.html +370 -232
  91. package/trajectory/src/assets/prism.min.js +13 -0
  92. package/trajectory/src/export.ts +4 -2
  93. package/trajectory/src/index.ts +124 -47
  94. package/trajectory/src/server.ts +114 -14
@@ -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");
@@ -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 }, ...raw.themes === void 0 ? {} : { themes: raw.themes } };
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 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";
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
- import { getAgentDir, parseFrontmatter } from "@earendil-works/pi-coding-agent";
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 dirname2, isAbsolute, join as join4 } from "node:path";
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 fileURLToPath(value);
2143
+ return fileURLToPath2(value);
1926
2144
  }
1927
2145
  if (typeof value === "string") {
1928
- if (value.startsWith("file:")) return fileURLToPath(value);
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 = dirname2(canonical);
2156
+ const starterDirectory = dirname3(canonical);
1939
2157
  if (basename2(canonical) !== "roles" || basename2(starterDirectory) !== "starter") return false;
1940
- const distributionDirectory = dirname2(starterDirectory);
1941
- const packageRoot = basename2(distributionDirectory) === "dist" ? dirname2(distributionDirectory) : distributionDirectory;
2158
+ const distributionDirectory = dirname3(starterDirectory);
2159
+ const packageRoot = basename2(distributionDirectory) === "dist" ? dirname3(distributionDirectory) : distributionDirectory;
1942
2160
  try {
1943
- const metadata = JSON.parse(readFileSync(join4(packageRoot, "package.json"), "utf8"));
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 === "pi-workflows:tool-timing";
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 = basename4(path);
2609
- return file.toLowerCase() === "skill.md" ? basename4(dirname4(path)) : file;
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 = getAgentDir2();
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 run = withoutAgentActivities(await withResolvedResources(withPiToolDescriptions(applySystemPrompts(value.run, prompts)), store.cwd));
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: overlay(value.run) } : value);
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, themes: true, connected: true, runs: [run], subagents: [] }],
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 serverHealthy(port) {
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
- return response.ok;
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 false;
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
- if (await serverHealthy(port)) return;
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
- if (await serverHealthy(existing.port)) {
2823
- if (existing.fingerprint === fingerprint) return existing;
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(isTimingEntry) : [];
3050
+ const entries = Array.isArray(value) ? value.filter(isTimingTranscriptEntry) : [];
2928
3051
  const retained = [];
2929
3052
  let bytes = 2;
2930
- for (const entry of entries) {
3053
+ for (let index = entries.length - 1; index >= 0; index -= 1) {
2931
3054
  let serialized;
2932
3055
  try {
2933
- serialized = JSON.stringify(entry);
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 >= 64 * 1024) break;
2939
- retained.push(entry);
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
- 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;
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
- return boundedLiveValue(publisher) ?? {};
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
- }, 1e3);
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, themes: input.themes, connected: true });
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 boundedTranscriptResult = (result) => {
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
- if (Buffer.byteLength(JSON.stringify(entries)) > TRAJECTORY_MAX_TRANSCRIPT_BYTES) return { status: "oversized", revision, entries: [], error: "Transcript is too large" };
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 ?? getAgentDir3());
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,