@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
@@ -0,0 +1,253 @@
1
+ import { atomicWriteFile } from "./persistence.js";
2
+ import { mkdirSync, readFileSync } from "node:fs";
3
+ import { homedir } from "node:os";
4
+ import { dirname, join, resolve } from "node:path";
5
+ import { fileURLToPath } from "node:url";
6
+ import { getAgentDir } from "@earendil-works/pi-coding-agent";
7
+ import { isContextFileScope } from "./types.js";
8
+ import { annotateModelAliasError, deepFreeze, errorText, fail, isNodeError, jsonValue, modelCapability, object, positiveInteger, resourcePatternHasMagic, unknownModel, validateModelAliases, validateResourcePattern, validWorkflowExtensionNamespace } from "./utils.js";
9
+ import { canonicalPath, workflowProjectSettingsPath } from "./paths.js";
10
+ export { workflowProjectSettingsPath } from "./paths.js";
11
+ const ROLE_DIRECTORY = "pi-extensible-workflows";
12
+ export const DEFAULT_SETTINGS = Object.freeze({ concurrency: 8, backgroundWidget: true });
13
+ export function workflowSettingsPath(agentDir = getAgentDir()) { return join(agentDir, ROLE_DIRECTORY, "settings.json"); }
14
+ function normalizedResourcePath(value, settingsPath) {
15
+ if (value === "*")
16
+ return value;
17
+ let expanded = value === "~" ? homedir() : value.startsWith("~/") || value.startsWith("~\\") ? join(homedir(), value.slice(2)) : value;
18
+ if (expanded.startsWith("file://"))
19
+ expanded = fileURLToPath(expanded);
20
+ const resolved = resolve(dirname(settingsPath), expanded);
21
+ if (expanded === "**" || expanded.startsWith("**/") || expanded.startsWith("**\\"))
22
+ return expanded;
23
+ if (resourcePatternHasMagic(expanded)) {
24
+ const magicIndex = resolved.search(/[*?\x5b\x5d{}()]/);
25
+ const separatorIndex = Math.max(resolved.lastIndexOf("/", magicIndex), resolved.lastIndexOf("\\", magicIndex));
26
+ const rootBoundary = separatorIndex === 0 || (separatorIndex === 2 && /^[A-Za-z]:[\\/]/.test(resolved));
27
+ const prefix = rootBoundary ? resolved.slice(0, separatorIndex + 1) : separatorIndex >= 0 ? resolved.slice(0, separatorIndex) : resolved;
28
+ const suffix = rootBoundary ? resolved.slice(separatorIndex + 1) : separatorIndex >= 0 ? resolved.slice(separatorIndex) : "";
29
+ return `${canonicalPath(prefix)}${suffix}`;
30
+ }
31
+ return canonicalPath(resolved);
32
+ }
33
+ export function validateSelectorList(value, path, kind, errorCode = "INVALID_SETTINGS", normalizeExtensions = true) {
34
+ if (value === undefined)
35
+ return undefined;
36
+ if (!Array.isArray(value))
37
+ fail(errorCode, `${path}.${kind} must be an array`);
38
+ const normalized = [];
39
+ for (const [index, entry] of value.entries()) {
40
+ if (typeof entry !== "string" || !entry.trim())
41
+ fail(errorCode, `${path}.${kind}[${String(index)}] must be a non-empty string`);
42
+ let selector = entry.trim();
43
+ if (kind === "extensions" && normalizeExtensions) {
44
+ const negated = selector.startsWith("!");
45
+ const body = negated ? selector.slice(1) : selector;
46
+ if (!body)
47
+ fail(errorCode, `${path}.${kind}[${String(index)}] must be a valid minimatch pattern: Empty minimatch pattern ${JSON.stringify(selector)}`);
48
+ try {
49
+ selector = `${negated ? "!" : ""}${normalizedResourcePath(body, path)}`;
50
+ }
51
+ catch (error) {
52
+ fail(errorCode, `${path}.${kind}[${String(index)}] must be a valid path: ${errorText(error)}`);
53
+ }
54
+ }
55
+ try {
56
+ validateResourcePattern(selector);
57
+ }
58
+ catch (error) {
59
+ fail(errorCode, `${path}.${kind}[${String(index)}] must be a valid minimatch pattern: ${errorText(error)}`);
60
+ }
61
+ normalized.push(selector);
62
+ }
63
+ return Object.freeze(normalized);
64
+ }
65
+ function validateWorktreePostCreateCommand(value, settingsPath) {
66
+ if (value === undefined)
67
+ return undefined;
68
+ if (!Array.isArray(value) || value.length === 0)
69
+ fail("INVALID_SETTINGS", `${settingsPath}.worktreePostCreateCommand must be a non-empty array`);
70
+ return Object.freeze(value.map((entry, index) => {
71
+ if (typeof entry !== "string" || !entry.trim())
72
+ fail("INVALID_SETTINGS", `${settingsPath}.worktreePostCreateCommand[${String(index)}] must be a non-empty string`);
73
+ return entry;
74
+ }));
75
+ }
76
+ function selectorsFromSettings(settings) {
77
+ return {
78
+ ...(settings.skills === undefined ? {} : { skills: settings.skills }),
79
+ ...(settings.extensions === undefined ? {} : { extensions: settings.extensions }),
80
+ ...(settings.tools === undefined ? {} : { tools: settings.tools }),
81
+ };
82
+ }
83
+ function selectorSet(value) { return { skills: [...(value?.skills ?? [])], extensions: [...(value?.extensions ?? [])], ...(value?.tools === undefined ? {} : { tools: [...value.tools] }) }; }
84
+ export function validateContextFileScopes(value, rolePath) {
85
+ if (value === undefined)
86
+ return undefined;
87
+ if (!Array.isArray(value) || !value.every(isContextFileScope))
88
+ fail("INVALID_METADATA", `${rolePath}.contextFiles must be an array containing only global, project, or cwd`);
89
+ return [...value];
90
+ }
91
+ export function validateWorkflowExtensionSettings(value, settingsPath, errorCode = "INVALID_SETTINGS") {
92
+ if (value === undefined)
93
+ return undefined;
94
+ const base = `${settingsPath}.extensionSettings`;
95
+ if (!object(value))
96
+ fail(errorCode, `${base} must be an object`);
97
+ const normalized = {};
98
+ for (const [namespace, raw] of Object.entries(value)) {
99
+ if (!validWorkflowExtensionNamespace(namespace))
100
+ fail(errorCode, `${base} contains an invalid namespace: ${namespace}`);
101
+ if (namespace === "herdr") {
102
+ if (!object(raw))
103
+ fail(errorCode, `${base}.herdr must be an object`);
104
+ if (Object.keys(raw).some((key) => key !== "enableFullyInspectableMode"))
105
+ fail(errorCode, `${base}.herdr contains an unsupported setting`);
106
+ if (raw.enableFullyInspectableMode !== undefined && typeof raw.enableFullyInspectableMode !== "boolean")
107
+ fail(errorCode, `${base}.herdr.enableFullyInspectableMode must be a boolean`);
108
+ normalized.herdr = Object.freeze({ ...(raw.enableFullyInspectableMode === undefined ? {} : { enableFullyInspectableMode: raw.enableFullyInspectableMode }) });
109
+ continue;
110
+ }
111
+ if (namespace === "trajectory") {
112
+ if (!object(raw))
113
+ fail(errorCode, `${base}.trajectory must be an object`);
114
+ if (Object.keys(raw).some((key) => key !== "port"))
115
+ fail(errorCode, `${base}.trajectory contains an unsupported setting`);
116
+ if (raw.port !== undefined && (!positiveInteger(raw.port) || raw.port > 65535))
117
+ fail(errorCode, `${base}.trajectory.port must be an integer from 1 to 65535`);
118
+ normalized.trajectory = Object.freeze({ ...(raw.port === undefined ? {} : { port: raw.port }) });
119
+ continue;
120
+ }
121
+ if (!jsonValue(raw))
122
+ fail(errorCode, `${base}.${namespace} must be JSON-compatible`);
123
+ normalized[namespace] = structuredClone(raw);
124
+ }
125
+ return deepFreeze(normalized);
126
+ }
127
+ function positiveRetentionInteger(value) { return positiveInteger(value) && Number.isSafeInteger(value); }
128
+ function validateRetention(value, settingsPath) {
129
+ if (value === undefined)
130
+ return undefined;
131
+ const base = `${settingsPath}.retention`;
132
+ if (!object(value))
133
+ fail("INVALID_SETTINGS", `${base} must be an object`);
134
+ const unknown = Object.keys(value).find((key) => key !== "olderThanDays" && key !== "maxTerminalRuns");
135
+ if (unknown)
136
+ fail("INVALID_SETTINGS", `Unknown retention setting at ${base}: ${unknown}`);
137
+ if (value.olderThanDays !== undefined && !positiveRetentionInteger(value.olderThanDays))
138
+ fail("INVALID_SETTINGS", `${base}.olderThanDays must be a positive integer`);
139
+ if (value.maxTerminalRuns !== undefined && !positiveRetentionInteger(value.maxTerminalRuns))
140
+ fail("INVALID_SETTINGS", `${base}.maxTerminalRuns must be a positive integer`);
141
+ return Object.freeze({ ...(value.olderThanDays === undefined ? {} : { olderThanDays: value.olderThanDays }), ...(value.maxTerminalRuns === undefined ? {} : { maxTerminalRuns: value.maxTerminalRuns }) });
142
+ }
143
+ function parseSettings(path, partial) {
144
+ let parsed;
145
+ try {
146
+ parsed = JSON.parse(readFileSync(path, "utf8"));
147
+ }
148
+ catch (error) {
149
+ if (isNodeError(error, "ENOENT"))
150
+ return partial ? Object.freeze({}) : DEFAULT_SETTINGS;
151
+ fail("CONFIG_ERROR", `Invalid workflow settings JSON at ${path}: ${errorText(error)}`);
152
+ }
153
+ if (!object(parsed))
154
+ fail("INVALID_SETTINGS", `Workflow settings at ${path} must be an object`);
155
+ const allowed = new Set(["concurrency", "modelAliases", "skills", "extensions", "extensionSettings", "tools", "retention", "worktreePostCreateCommand", ...(partial ? [] : ["backgroundWidget"])]);
156
+ const unknown = Object.keys(parsed).find((key) => !allowed.has(key));
157
+ if (Object.prototype.hasOwnProperty.call(parsed, "disabledAgentResources"))
158
+ fail("INVALID_SETTINGS", `disabledAgentResources is no longer supported; use skills, extensions, and tools selectors (settings: ${path})`);
159
+ if (unknown)
160
+ fail("INVALID_SETTINGS", `Unknown workflow setting at ${path}: ${unknown}`);
161
+ const concurrency = parsed.concurrency === undefined ? (partial ? undefined : DEFAULT_SETTINGS.concurrency) : parsed.concurrency;
162
+ if (concurrency !== undefined && (!positiveInteger(concurrency) || concurrency > 16))
163
+ fail("INVALID_SETTINGS", `${path}.concurrency must be an integer from 1 to 16`);
164
+ const backgroundWidget = parsed.backgroundWidget === undefined ? (partial ? undefined : DEFAULT_SETTINGS.backgroundWidget) : parsed.backgroundWidget;
165
+ if (backgroundWidget !== undefined && typeof backgroundWidget !== "boolean")
166
+ fail("INVALID_SETTINGS", `${path}.backgroundWidget must be a boolean`);
167
+ const modelAliases = parsed.modelAliases === undefined ? undefined : validateModelAliases(parsed.modelAliases, path);
168
+ const skills = validateSelectorList(parsed.skills, path, "skills");
169
+ const tools = validateSelectorList(parsed.tools, path, "tools");
170
+ const extensions = validateSelectorList(parsed.extensions, path, "extensions");
171
+ const extensionSettings = parsed.extensionSettings === undefined ? undefined : validateWorkflowExtensionSettings(parsed.extensionSettings, path, "INVALID_SETTINGS");
172
+ const retention = validateRetention(parsed.retention, path);
173
+ const worktreePostCreateCommand = validateWorktreePostCreateCommand(parsed.worktreePostCreateCommand, path);
174
+ return Object.freeze({
175
+ ...(concurrency === undefined ? {} : { concurrency }), ...(backgroundWidget === undefined ? {} : { backgroundWidget }), ...(modelAliases === undefined ? {} : { modelAliases }),
176
+ ...(skills === undefined ? {} : { skills }), ...(extensions === undefined ? {} : { extensions }),
177
+ ...(extensionSettings === undefined ? {} : { extensionSettings }), ...(tools === undefined ? {} : { tools }), ...(retention === undefined ? {} : { retention }),
178
+ ...(worktreePostCreateCommand === undefined ? {} : { worktreePostCreateCommand }),
179
+ });
180
+ }
181
+ export function loadSettings(path = workflowSettingsPath()) { return parseSettings(path, false); }
182
+ export function loadSettingsOverrides(path) { return parseSettings(path, true); }
183
+ export function resolveWorkflowSettings(cwd, projectTrusted, globalSettingsPath = workflowSettingsPath()) {
184
+ const projectSettingsPath = workflowProjectSettingsPath(cwd);
185
+ const global = loadSettings(globalSettingsPath);
186
+ const project = projectTrusted ? loadSettingsOverrides(projectSettingsPath) : Object.freeze({});
187
+ const projectHas = (key) => Object.prototype.hasOwnProperty.call(project, key);
188
+ const sourceFor = (key) => projectHas(key) ? projectSettingsPath : globalSettingsPath;
189
+ const globalSelectors = selectorsFromSettings(global);
190
+ const projectSelectors = selectorsFromSettings(project);
191
+ const effectiveSelectors = selectorSet({
192
+ skills: [...(globalSelectors.skills ?? []), ...(projectSelectors.skills ?? [])],
193
+ extensions: [...(globalSelectors.extensions ?? []), ...(projectSelectors.extensions ?? [])],
194
+ ...(globalSelectors.tools === undefined && projectSelectors.tools === undefined ? {} : { tools: [...(globalSelectors.tools ?? []), ...(projectSelectors.tools ?? [])] }),
195
+ });
196
+ const hasExtensionSelectors = global.extensions !== undefined || project.extensions !== undefined;
197
+ const extensionSettings = projectHas("extensionSettings") ? project.extensionSettings : global.extensionSettings;
198
+ const sources = {
199
+ concurrency: projectHas("concurrency") ? projectSettingsPath : globalSettingsPath,
200
+ modelAliases: projectHas("modelAliases") ? projectSettingsPath : globalSettingsPath,
201
+ skills: sourceFor("skills"), extensions: sourceFor("extensions"), tools: sourceFor("tools"),
202
+ ...(extensionSettings === undefined ? {} : { extensionSettings: projectHas("extensionSettings") ? projectSettingsPath : globalSettingsPath }),
203
+ ...(project.retention === undefined && global.retention === undefined ? {} : { retention: project.retention === undefined ? globalSettingsPath : projectSettingsPath }),
204
+ ...(projectHas("worktreePostCreateCommand") || global.worktreePostCreateCommand !== undefined ? { worktreePostCreateCommand: projectHas("worktreePostCreateCommand") ? projectSettingsPath : globalSettingsPath } : {}),
205
+ };
206
+ const effective = Object.freeze({
207
+ concurrency: project.concurrency ?? global.concurrency,
208
+ backgroundWidget: global.backgroundWidget ?? true,
209
+ ...(projectHas("modelAliases") ? { modelAliases: project.modelAliases } : global.modelAliases === undefined ? {} : { modelAliases: global.modelAliases }),
210
+ ...(effectiveSelectors.skills.length ? { skills: effectiveSelectors.skills } : global.skills !== undefined || project.skills !== undefined ? { skills: effectiveSelectors.skills } : {}),
211
+ ...(hasExtensionSelectors ? { extensions: effectiveSelectors.extensions } : {}),
212
+ ...(extensionSettings === undefined ? {} : { extensionSettings }),
213
+ ...(effectiveSelectors.tools?.length ? { tools: effectiveSelectors.tools } : global.tools !== undefined || project.tools !== undefined ? { tools: effectiveSelectors.tools } : {}),
214
+ ...((project.retention ?? global.retention) === undefined ? {} : { retention: project.retention ?? global.retention }),
215
+ ...(projectHas("worktreePostCreateCommand") ? { worktreePostCreateCommand: project.worktreePostCreateCommand } : global.worktreePostCreateCommand === undefined ? {} : { worktreePostCreateCommand: global.worktreePostCreateCommand }),
216
+ });
217
+ return { globalSettingsPath, projectSettingsPath, projectTrusted, global, project, effective, sources };
218
+ }
219
+ export function validateModelAliasAvailability(aliases, names, availableModels, knownModels, settingsPath) {
220
+ for (const name of names) {
221
+ try {
222
+ const target = modelCapability(name, aliases, knownModels, settingsPath);
223
+ if (!availableModels.has(target))
224
+ unknownModel(name, target, settingsPath);
225
+ }
226
+ catch (error) {
227
+ throw annotateModelAliasError(error, name);
228
+ }
229
+ }
230
+ }
231
+ export function resolveAgentResourcePolicy(cwd, projectTrusted, globalSettingsPath = workflowSettingsPath()) {
232
+ const resolved = resolveWorkflowSettings(cwd, projectTrusted, globalSettingsPath);
233
+ const global = selectorSet(selectorsFromSettings(resolved.global));
234
+ const project = selectorSet(selectorsFromSettings(resolved.project));
235
+ const effective = selectorSet(selectorsFromSettings(resolved.effective));
236
+ return { globalSettingsPath: resolved.globalSettingsPath, projectSettingsPath: resolved.projectSettingsPath, projectTrusted, global, project, effective, unmatchedSkills: [], unmatchedExtensions: [], unmatchedTools: [], selectorSources: { global: selectorsFromSettings(resolved.global), project: selectorsFromSettings(resolved.project) } };
237
+ }
238
+ export function saveModelAliases(path = workflowSettingsPath(), aliases = {}) {
239
+ const normalized = validateModelAliases(aliases, path);
240
+ let parsed = {};
241
+ try {
242
+ loadSettings(path);
243
+ parsed = JSON.parse(readFileSync(path, "utf8"));
244
+ }
245
+ catch (error) {
246
+ if (!isNodeError(error, "ENOENT"))
247
+ throw error;
248
+ }
249
+ if (!object(parsed))
250
+ fail("INVALID_SETTINGS", `Workflow settings at ${path} must be an object`);
251
+ mkdirSync(dirname(path), { recursive: true });
252
+ atomicWriteFile(path, `${JSON.stringify({ ...parsed, modelAliases: normalized }, null, 2)}\n`, true);
253
+ }
@@ -1,10 +1,34 @@
1
- import { type AwaitingCheckpoint, type EffectiveSystemPrompt, type PersistedRun } from "./persistence.js";
2
- import type { AgentAttemptSummary, JsonValue, LaunchSnapshot, WorkflowAgentSessionReference } from "./types.js";
1
+ import { type AwaitingCheckpoint, type CompletedOperation, type EffectiveSystemPrompt, type PersistedRun } from "./persistence.js";
2
+ import type { AgentAttemptSummary, AgentRecord, JsonValue, LaunchSnapshot, WorkflowAgentSessionReference } from "./types.js";
3
3
  import { type SubagentProgress, type SubagentRunRequest, type SubagentStatus } from "../subagents/src/contracts.js";
4
4
  import type { TrajectoryAction, TrajectoryTarget } from "./trajectory-contracts.js";
5
5
  export declare const TRAJECTORY_MAX_TRANSCRIPT_BYTES: number;
6
+ export type TrajectoryAgentOutput = {
7
+ readonly status: "available";
8
+ readonly value: JsonValue;
9
+ readonly bytes: number;
10
+ } | {
11
+ readonly status: "pending";
12
+ } | {
13
+ readonly status: "failed" | "cancelled";
14
+ readonly code: string;
15
+ readonly message: string;
16
+ } | {
17
+ readonly status: "truncated";
18
+ readonly kind: "result" | "failure";
19
+ readonly bytes: number;
20
+ readonly path?: string;
21
+ } | {
22
+ readonly status: "unavailable";
23
+ };
24
+ export type TrajectoryAgent = AgentRecord & {
25
+ readonly output: TrajectoryAgentOutput;
26
+ };
27
+ export type TrajectoryRunRecord = Omit<PersistedRun, "agents"> & {
28
+ readonly agents: readonly TrajectoryAgent[];
29
+ };
6
30
  export type TrajectoryRun = {
7
- run: PersistedRun;
31
+ run: TrajectoryRunRecord;
8
32
  snapshot: Readonly<LaunchSnapshot>;
9
33
  awaiting: readonly AwaitingCheckpoint[];
10
34
  createdAt?: string;
@@ -58,6 +82,7 @@ export type TrajectorySubagent = {
58
82
  readonly code: string;
59
83
  readonly message: string;
60
84
  } | TrajectorySubagentArtifact;
85
+ output: TrajectoryAgentOutput;
61
86
  transcript: readonly unknown[];
62
87
  };
63
88
  export type TrajectorySubagentMetadata = Omit<TrajectorySubagent, "transcript"> & {
@@ -104,7 +129,6 @@ export type TrajectoryPublisherInput = {
104
129
  cwd: string;
105
130
  sessionId: string;
106
131
  port?: number;
107
- themes: boolean;
108
132
  loadRuns: TrajectoryRunLoader;
109
133
  loadSubagents: TrajectorySubagentLoader;
110
134
  loadMetadata: TrajectoryPublisherMetadataLoader;
@@ -120,6 +144,7 @@ export declare function withPiToolDescriptionsForTools(tools: readonly string[],
120
144
  }[];
121
145
  export declare function withResolvedAttemptResources(attempt: AgentAttemptSummary, cwd: string): Promise<AgentAttemptSummary>;
122
146
  export declare function withResolvedResources(run: PersistedRun, cwd: string): Promise<PersistedRun>;
147
+ export declare function applyTrajectoryAgentOutputs(run: PersistedRun, operations: readonly CompletedOperation[]): TrajectoryRunRecord;
123
148
  export declare function loadTrajectoryRuns(cwd: string, sessionId: string, home?: string, overlay?: (run: PersistedRun) => PersistedRun): Promise<readonly TrajectoryRun[]>;
124
149
  export declare function createTrajectoryRunLoader(cwd: string, sessionId: string, home?: string, overlay?: (run: PersistedRun) => PersistedRun): TrajectoryRunLoader;
125
150
  export declare function createTrajectoryRunMetadataLoader(cwd: string, sessionId: string, home?: string, overlay?: (run: PersistedRun) => PersistedRun): TrajectoryRunMetadataLoader;
@@ -11,6 +11,23 @@ import { isNodeError, jsonValue, object, resourcePatternHasMagic, selectResource
11
11
  import { canonicalPath, sameFilesystemPath } from "./paths.js";
12
12
  export const TRAJECTORY_MAX_TRANSCRIPT_BYTES = 2 * 1024 * 1024;
13
13
  const TRAJECTORY_MAX_NON_TIMING_ENTRIES = 400;
14
+ function subagentOutput(options) {
15
+ if (options.resultTruncated)
16
+ return { status: "truncated", kind: "result", path: options.resultPath, bytes: options.resultBytes ?? 0 };
17
+ if (options.result !== undefined)
18
+ return { status: "available", value: options.result, bytes: serializedJsonBytes(options.result) };
19
+ if (options.failureTruncated)
20
+ return { status: "truncated", kind: "failure", path: options.failurePath, bytes: options.failureBytes ?? 0 };
21
+ if (options.failure !== undefined)
22
+ return { status: "failed", code: options.failure.code, message: options.failure.message };
23
+ if (options.state === "failed" || options.state === "stopped") {
24
+ const error = options.statusError ?? options.attempt?.error;
25
+ return { status: options.state === "stopped" ? "cancelled" : "failed", code: error?.code ?? (options.state === "stopped" ? "CANCELLED" : "AGENT_FAILED"), message: error?.message ?? `Subagent ${options.state}` };
26
+ }
27
+ if (options.state === "completed")
28
+ return { status: "unavailable" };
29
+ return { status: "pending" };
30
+ }
14
31
  export { isTrajectoryAction, isTrajectoryTarget, trajectoryActionError } from "./trajectory-contracts.js";
15
32
  function sessionFile(reference) {
16
33
  const locator = reference?.locator;
@@ -254,11 +271,48 @@ function withoutAgentActivities(run) {
254
271
  });
255
272
  return agents.every((agent, index) => agent === run.agents[index]) ? run : { ...run, agents };
256
273
  }
274
+ function serializedJsonBytes(value) {
275
+ const serialized = JSON.stringify(value);
276
+ return Buffer.byteLength(serialized, "utf8");
277
+ }
278
+ function failedAgentOutput(agent) {
279
+ const error = [...(agent.attemptDetails ?? [])].reverse().find((attempt) => attempt.error)?.error;
280
+ const status = agent.state === "cancelled" ? "cancelled" : "failed";
281
+ return { status, code: error?.code ?? (status === "cancelled" ? "CANCELLED" : "AGENT_FAILED"), message: error?.message ?? `Agent ${agent.state}` };
282
+ }
283
+ function outputForAgent(agent, operation) {
284
+ if (operation !== undefined) {
285
+ const bytes = serializedJsonBytes(operation.value);
286
+ return bytes > DEFAULT_MAX_BYTES ? { status: "truncated", kind: "result", bytes } : { status: "available", value: operation.value, bytes };
287
+ }
288
+ if (agent.state === "failed" || agent.state === "cancelled")
289
+ return failedAgentOutput(agent);
290
+ if (agent.state === "completed")
291
+ return { status: "unavailable" };
292
+ return { status: "pending" };
293
+ }
294
+ export function applyTrajectoryAgentOutputs(run, operations) {
295
+ const byPath = new Map(operations.map((operation) => [operation.path, operation]));
296
+ const agents = run.agents.map((agent) => ({ ...agent, output: outputForAgent(agent, agent.resultPath === undefined ? undefined : byPath.get(agent.resultPath)) }));
297
+ return { ...run, agents };
298
+ }
299
+ function overlayTrajectoryRun(run, overlay) {
300
+ const overlaid = overlay(run);
301
+ const outputs = new Map(run.agents.map((agent) => [agent.id, agent.output]));
302
+ const agents = overlaid.agents.map((agent) => {
303
+ const previous = outputs.get(agent.id);
304
+ const output = previous?.status === "available" || previous?.status === "truncated" ? previous : outputForAgent(agent, undefined);
305
+ return { ...agent, output };
306
+ });
307
+ return { ...overlaid, agents };
308
+ }
257
309
  async function loadTrajectoryRun(store, includeTranscripts = true) {
258
310
  const value = await store.load();
259
311
  const summary = await store.loadSummary().catch(() => undefined);
260
312
  const prompts = await store.systemPrompts().catch(() => []);
261
- const run = withoutAgentActivities(await withResolvedResources(withPiToolDescriptions(applySystemPrompts(value.run, prompts)), store.cwd));
313
+ const prepared = withoutAgentActivities(await withResolvedResources(withPiToolDescriptions(applySystemPrompts(value.run, prompts)), store.cwd));
314
+ const operations = await store.replayableOperations().catch(() => []);
315
+ const run = applyTrajectoryAgentOutputs(prepared, operations);
262
316
  return { run, snapshot: value.snapshot, awaiting: await store.awaitingCheckpoints(), ...(summary?.createdAt === undefined ? {} : { createdAt: summary.createdAt }), transcripts: includeTranscripts ? await runTranscripts(run) : {} };
263
317
  }
264
318
  export async function loadTrajectoryRuns(cwd, sessionId, home = homedir(), overlay) {
@@ -266,7 +320,7 @@ export async function loadTrajectoryRuns(cwd, sessionId, home = homedir(), overl
266
320
  for (const runId of await listRunIds(cwd, sessionId, home, false)) {
267
321
  try {
268
322
  const value = await loadTrajectoryRun(new RunStore(cwd, sessionId, runId, home));
269
- loaded.push(overlay ? { ...value, run: overlay(value.run) } : value);
323
+ loaded.push(overlay ? { ...value, run: overlayTrajectoryRun(value.run, overlay) } : value);
270
324
  }
271
325
  catch { /* Ignore corrupt or concurrently removed runs. */ }
272
326
  }
@@ -305,7 +359,7 @@ function createCachedRunLoader(cwd, sessionId, home, load, transcriptSignature,
305
359
  entry = { value, stateMtimeMs: (await fileMtime(join(store.directory, "state.json"))) ?? 0, journalMtimeMs: (await fileMtime(join(store.directory, "journal.json"))) ?? 0, transcripts };
306
360
  cache.set(runId, entry);
307
361
  }
308
- loaded.push(overlay ? { ...entry.value, run: overlay(entry.value.run) } : entry.value);
362
+ loaded.push(overlay ? { ...entry.value, run: overlayTrajectoryRun(entry.value.run, overlay) } : entry.value);
309
363
  }
310
364
  catch {
311
365
  cache.delete(runId); /* Ignore corrupt or concurrently removed runs. */
@@ -513,6 +567,7 @@ async function loadTrajectorySubagent(directory, cwd, sessionId, includeTranscri
513
567
  ...(attempt === undefined ? {} : { attempt }),
514
568
  ...(result === undefined ? {} : { result }),
515
569
  ...(failure === undefined ? {} : { failure }),
570
+ output: subagentOutput({ state: status.state, statusError: status.error, attempt, result: resultValue === undefined ? undefined : boundedSubagentJson(resultValue), resultTruncated, resultPath, resultBytes, failure: failureValue === undefined ? undefined : subagentErrorValue(failureValue), failureTruncated, failurePath, failureBytes }),
516
571
  };
517
572
  }
518
573
  catch {
@@ -190,7 +190,6 @@ export interface HerdrExtensionSettings {
190
190
  }
191
191
  export interface TrajectoryExtensionSettings {
192
192
  port?: number;
193
- themes?: boolean;
194
193
  }
195
194
  export interface WorkflowExtensionSettings {
196
195
  readonly [namespace: string]: JsonValue;
@@ -1,4 +1,4 @@
1
- import { WorkflowError, type JsonValue, type ModelSpec, type ThinkingLevel, type WorkflowErrorCode, type WorkflowExtensionSettings } from "./types.js";
1
+ import { WorkflowError, type JsonSchema, type JsonValue, type ModelSpec, type ThinkingLevel, type WorkflowErrorCode, type WorkflowExtensionSettings } from "./types.js";
2
2
  export declare class SerialLane {
3
3
  #private;
4
4
  run<T>(task: () => Promise<T>): Promise<T>;
@@ -55,3 +55,4 @@ export declare function createLaunchSnapshot(input: Omit<import("./types.js").La
55
55
  identityVersion?: number;
56
56
  }): Readonly<import("./types.js").LaunchSnapshot>;
57
57
  export declare function loadLaunchSnapshot(input: import("./types.js").LaunchSnapshot): Readonly<import("./types.js").LaunchSnapshot>;
58
+ export declare function validateSchema(schema: unknown, at?: string): asserts schema is JsonSchema;
package/dist/src/utils.js CHANGED
@@ -223,3 +223,13 @@ export function resourcePatternHasMagic(pattern) { return /[*?\x5b\x5d{}()]/.tes
223
223
  export function unmatchedResourcePatterns(patterns, resources) { return patterns.filter((pattern) => !resources.some((resource) => resourcePatternMatches(resource, pattern))); }
224
224
  export function createLaunchSnapshot(input) { return deepFreeze(structuredClone({ ...input, identityVersion: input.identityVersion ?? LAUNCH_SNAPSHOT_IDENTITY_VERSION })); }
225
225
  export function loadLaunchSnapshot(input) { return deepFreeze(structuredClone(input)); }
226
+ export function validateSchema(schema, at = "schema") {
227
+ if (!object(schema) || Object.getPrototypeOf(schema) !== Object.prototype || !jsonValue(schema))
228
+ fail("INVALID_SCHEMA", `${at} must be a plain JSON-compatible Schema object`);
229
+ if (typeof schema.type !== "string" && !Array.isArray(schema.type) && schema.$ref === undefined && schema.anyOf === undefined && schema.oneOf === undefined && schema.allOf === undefined && schema.const === undefined && schema.enum === undefined)
230
+ fail("INVALID_SCHEMA", `${at} has no JSON Schema shape`);
231
+ if (schema.required !== undefined && (!Array.isArray(schema.required) || schema.required.some((key) => typeof key !== "string")))
232
+ fail("INVALID_SCHEMA", `${at}.required must be an array of strings`);
233
+ if (schema.properties !== undefined && !object(schema.properties))
234
+ fail("INVALID_SCHEMA", `${at}.properties must be an object`);
235
+ }
@@ -1,24 +1,12 @@
1
- import type { AgentDefinition, AgentResourcePolicy, CheckpointInput, JsonSchema, JsonValue, PreflightCapabilities, PreflightResult, ShellOptions, StaticWorkflowCall, ValidatedWorkflowLaunch, WorkflowExtensionSettings, WorkflowMetadata, WorkflowRoleDirectoryRegistration, WorkflowSettings, WorkflowSettingsOverrides, WorkflowSettingsResolution, WorkflowValidationContext, WorkflowValidationParameters } from "./types.js";
1
+ import type { CheckpointInput, JsonValue, PreflightCapabilities, PreflightResult, ShellOptions, StaticWorkflowCall, ValidatedWorkflowLaunch, WorkflowMetadata, WorkflowValidationContext, WorkflowValidationParameters } from "./types.js";
2
2
  import type { WorkflowRegistryApi } from "./registry.js";
3
- export { workflowProjectSettingsPath } from "./paths.js";
4
- export declare const DEFAULT_SETTINGS: Readonly<WorkflowSettings>;
3
+ export { validateSchema } from "./utils.js";
4
+ export { loadAgentDefinitions, loadProjectAgentDefinitions, parseRoleMarkdown, workflowRoleDirectories } from "./roles.js";
5
+ export type { WorkflowRoleDirectoryInput } from "./roles.js";
6
+ export { DEFAULT_SETTINGS, loadSettings, loadSettingsOverrides, resolveAgentResourcePolicy, resolveWorkflowSettings, saveModelAliases, validateContextFileScopes, validateModelAliasAvailability, validateSelectorList, validateWorkflowExtensionSettings, workflowProjectSettingsPath, workflowSettingsPath } from "./settings.js";
5
7
  export declare function validateCheckpoint(value: unknown): CheckpointInput;
6
- export declare function workflowSettingsPath(agentDir?: string): string;
7
- export declare function validateWorkflowExtensionSettings(value: unknown, settingsPath: string, errorCode?: "INVALID_SETTINGS" | "INVALID_METADATA", applyDefaults?: boolean): WorkflowExtensionSettings | undefined;
8
- export declare function loadSettings(path?: string): Readonly<WorkflowSettings>;
9
- export declare function loadSettingsOverrides(path: string): Readonly<WorkflowSettingsOverrides>;
10
- export declare function resolveWorkflowSettings(cwd: string, projectTrusted: boolean, globalSettingsPath?: string): WorkflowSettingsResolution;
11
- export declare function validateModelAliasAvailability(aliases: Readonly<Record<string, string>>, names: readonly string[], availableModels: ReadonlySet<string>, knownModels: ReadonlySet<string>, settingsPath?: string): void;
12
- export declare function resolveAgentResourcePolicy(cwd: string, projectTrusted: boolean, globalSettingsPath?: string): AgentResourcePolicy;
13
- export declare function saveModelAliases(path?: string, aliases?: Readonly<Record<string, string>>): void;
14
- export declare function parseRoleMarkdown(content: string, strict?: boolean, rolePath?: string): AgentDefinition;
15
- export declare function workflowRoleDirectories(agentDir?: string): readonly string[];
16
- export type WorkflowRoleDirectoryInput = string | WorkflowRoleDirectoryRegistration;
17
- export declare function loadProjectAgentDefinitions(cwd: string): Readonly<Record<string, AgentDefinition>>;
18
- export declare function loadAgentDefinitions(cwd: string, agentDir?: string, projectTrusted?: boolean, extensionRoleDirectories?: readonly WorkflowRoleDirectoryInput[]): Readonly<Record<string, AgentDefinition>>;
19
8
  export declare function instrumentWorkflow(script: string): string;
20
9
  export declare function workflowPrompt(template: string, values: Readonly<Record<string, JsonValue>>): string;
21
- export declare function validateSchema(schema: unknown, at?: string): asserts schema is JsonSchema;
22
10
  export declare function validateAgentOptions(value: unknown): Readonly<Record<string, JsonValue>>;
23
11
  export declare function validateShellOptions(value: unknown): ShellOptions;
24
12
  export declare function validateShellCommand(value: unknown): string;