@marcoscale98/pi-extensible-workflows 5.14.1-fork.1 → 5.19.1-fork.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +135 -0
- package/dist/src/agent-execution.js +114 -87
- package/dist/src/background-widget.d.ts +61 -0
- package/dist/src/background-widget.js +23 -16
- package/dist/src/decoders.js +28 -1
- package/dist/src/execution.d.ts +4 -2
- package/dist/src/execution.js +43 -2
- package/dist/src/host-delivery.js +1 -0
- package/dist/src/host-navigator.d.ts +18 -0
- package/dist/src/host-navigator.js +71 -13
- package/dist/src/host-recovery.d.ts +5 -2
- package/dist/src/host-recovery.js +3 -3
- package/dist/src/host-view.d.ts +21 -1
- package/dist/src/host-view.js +138 -47
- package/dist/src/host.d.ts +2 -2
- package/dist/src/host.js +246 -60
- package/dist/src/index.d.ts +3 -1
- package/dist/src/index.js +1346 -622
- package/dist/src/index.js.map +3 -3
- package/dist/src/paths.d.ts +2 -0
- package/dist/src/paths.js +2 -0
- package/dist/src/registry.js +3 -2
- package/dist/src/roles.d.ts +70 -0
- package/dist/src/roles.js +326 -0
- package/dist/src/script-tools.d.ts +27 -0
- package/dist/src/script-tools.js +87 -0
- package/dist/src/settings.d.ts +26 -0
- package/dist/src/settings.js +277 -0
- package/dist/src/trajectory.d.ts +39 -4
- package/dist/src/trajectory.js +82 -5
- package/dist/src/types.d.ts +30 -3
- package/dist/src/types.js +1 -1
- package/dist/src/utils.d.ts +21 -1
- package/dist/src/utils.js +40 -0
- package/dist/src/validation.d.ts +10 -17
- package/dist/src/validation.js +29 -458
- package/dist/starter/index.js +0 -2
- package/dist/starter/index.js.map +1 -1
- package/dist/starter/review-loop.d.ts +0 -13
- package/dist/starter/review-loop.js +0 -2
- package/dist/subagents/index.js +1344 -798
- package/dist/subagents/index.js.map +3 -3
- package/dist/subagents/src/contracts.d.ts +49 -29
- package/dist/subagents/src/contracts.js +18 -7
- package/dist/subagents/src/index.d.ts +1 -1
- package/dist/subagents/src/index.js +15 -7
- package/dist/subagents/src/manager.js +32 -13
- package/dist/subagents/src/navigator.js +396 -247
- package/dist/subagents/src/view.d.ts +24 -2
- package/dist/subagents/src/view.js +107 -80
- package/dist/trajectory/assets/index.html +370 -232
- package/dist/trajectory/assets/prism.min.js +13 -0
- package/dist/trajectory/index.js +445 -277
- package/dist/trajectory/index.js.map +3 -3
- package/dist/trajectory/src/assets/index.html +370 -232
- package/dist/trajectory/src/assets/prism.min.js +13 -0
- package/dist/trajectory/src/export.js +4 -2
- package/dist/trajectory/src/index.d.ts +2 -0
- package/dist/trajectory/src/index.js +151 -60
- package/dist/trajectory/src/server.js +138 -11
- package/dist/trajectory/src/server.js.map +2 -2
- package/package.json +7 -6
- package/skills/pi-extensible-workflows/SKILL.md +2 -0
- package/src/agent-execution.ts +110 -80
- package/src/background-widget.ts +26 -19
- package/src/decoders.ts +22 -1
- package/src/execution.ts +41 -4
- package/src/host-delivery.ts +1 -0
- package/src/host-navigator.ts +58 -14
- package/src/host-recovery.ts +8 -7
- package/src/host-view.ts +116 -42
- package/src/host.ts +223 -55
- package/src/index.ts +3 -1
- package/src/paths.ts +3 -0
- package/src/registry.ts +3 -2
- package/src/roles.ts +327 -0
- package/src/script-tools.ts +76 -0
- package/src/settings.ts +224 -0
- package/src/trajectory.ts +97 -9
- package/src/types.ts +12 -5
- package/src/utils.ts +36 -1
- package/src/validation.ts +30 -356
- package/starter/review-loop.ts +0 -2
- package/subagents/README.md +3 -3
- package/subagents/src/contracts.ts +22 -7
- package/subagents/src/index.ts +18 -7
- package/subagents/src/manager.ts +31 -13
- package/subagents/src/navigator.ts +336 -180
- package/subagents/src/view.ts +108 -76
- package/trajectory/src/assets/index.html +370 -232
- package/trajectory/src/assets/prism.min.js +13 -0
- package/trajectory/src/export.ts +4 -2
- package/trajectory/src/index.ts +124 -47
- package/trajectory/src/server.ts +114 -14
|
@@ -0,0 +1,277 @@
|
|
|
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
|
+
// Built-in extensions are named `builtin:<name>`, not by a path.
|
|
16
|
+
if (value === "*" || value.startsWith("builtin:"))
|
|
17
|
+
return value;
|
|
18
|
+
let expanded = value === "~" ? homedir() : value.startsWith("~/") || value.startsWith("~\\") ? join(homedir(), value.slice(2)) : value;
|
|
19
|
+
if (expanded.startsWith("file://"))
|
|
20
|
+
expanded = fileURLToPath(expanded);
|
|
21
|
+
const resolved = resolve(dirname(settingsPath), expanded);
|
|
22
|
+
if (expanded === "**" || expanded.startsWith("**/") || expanded.startsWith("**\\"))
|
|
23
|
+
return expanded;
|
|
24
|
+
if (resourcePatternHasMagic(expanded)) {
|
|
25
|
+
const magicIndex = resolved.search(/[*?\x5b\x5d{}()]/);
|
|
26
|
+
const separatorIndex = Math.max(resolved.lastIndexOf("/", magicIndex), resolved.lastIndexOf("\\", magicIndex));
|
|
27
|
+
const rootBoundary = separatorIndex === 0 || (separatorIndex === 2 && /^[A-Za-z]:[\\/]/.test(resolved));
|
|
28
|
+
const prefix = rootBoundary ? resolved.slice(0, separatorIndex + 1) : separatorIndex >= 0 ? resolved.slice(0, separatorIndex) : resolved;
|
|
29
|
+
const suffix = rootBoundary ? resolved.slice(separatorIndex + 1) : separatorIndex >= 0 ? resolved.slice(separatorIndex) : "";
|
|
30
|
+
return `${canonicalPath(prefix)}${suffix}`;
|
|
31
|
+
}
|
|
32
|
+
return canonicalPath(resolved);
|
|
33
|
+
}
|
|
34
|
+
export function validateSelectorList(value, path, kind, errorCode = "INVALID_SETTINGS", normalizeExtensions = true) {
|
|
35
|
+
if (value === undefined)
|
|
36
|
+
return undefined;
|
|
37
|
+
if (!Array.isArray(value))
|
|
38
|
+
fail(errorCode, `${path}.${kind} must be an array`);
|
|
39
|
+
const normalized = [];
|
|
40
|
+
for (const [index, entry] of value.entries()) {
|
|
41
|
+
if (typeof entry !== "string" || !entry.trim())
|
|
42
|
+
fail(errorCode, `${path}.${kind}[${String(index)}] must be a non-empty string`);
|
|
43
|
+
let selector = entry.trim();
|
|
44
|
+
if (kind === "extensions" && normalizeExtensions) {
|
|
45
|
+
const negated = selector.startsWith("!");
|
|
46
|
+
const body = negated ? selector.slice(1) : selector;
|
|
47
|
+
if (!body)
|
|
48
|
+
fail(errorCode, `${path}.${kind}[${String(index)}] must be a valid minimatch pattern: Empty minimatch pattern ${JSON.stringify(selector)}`);
|
|
49
|
+
try {
|
|
50
|
+
selector = `${negated ? "!" : ""}${normalizedResourcePath(body, path)}`;
|
|
51
|
+
}
|
|
52
|
+
catch (error) {
|
|
53
|
+
fail(errorCode, `${path}.${kind}[${String(index)}] must be a valid path: ${errorText(error)}`);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
try {
|
|
57
|
+
validateResourcePattern(selector);
|
|
58
|
+
}
|
|
59
|
+
catch (error) {
|
|
60
|
+
fail(errorCode, `${path}.${kind}[${String(index)}] must be a valid minimatch pattern: ${errorText(error)}`);
|
|
61
|
+
}
|
|
62
|
+
normalized.push(selector);
|
|
63
|
+
}
|
|
64
|
+
return Object.freeze(normalized);
|
|
65
|
+
}
|
|
66
|
+
function validateWorktreePostCreateCommand(value, settingsPath) {
|
|
67
|
+
if (value === undefined)
|
|
68
|
+
return undefined;
|
|
69
|
+
if (!Array.isArray(value) || value.length === 0)
|
|
70
|
+
fail("INVALID_SETTINGS", `${settingsPath}.worktreePostCreateCommand must be a non-empty array`);
|
|
71
|
+
return Object.freeze(value.map((entry, index) => {
|
|
72
|
+
if (typeof entry !== "string" || !entry.trim())
|
|
73
|
+
fail("INVALID_SETTINGS", `${settingsPath}.worktreePostCreateCommand[${String(index)}] must be a non-empty string`);
|
|
74
|
+
return entry;
|
|
75
|
+
}));
|
|
76
|
+
}
|
|
77
|
+
function selectorsFromSettings(settings) {
|
|
78
|
+
return {
|
|
79
|
+
...(settings.skills === undefined ? {} : { skills: settings.skills }),
|
|
80
|
+
...(settings.extensions === undefined ? {} : { extensions: settings.extensions }),
|
|
81
|
+
...(settings.tools === undefined ? {} : { tools: settings.tools }),
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
function selectorSet(value) { return { skills: [...(value?.skills ?? [])], extensions: [...(value?.extensions ?? [])], ...(value?.tools === undefined ? {} : { tools: [...value.tools] }) }; }
|
|
85
|
+
export function validateContextFileScopes(value, rolePath) {
|
|
86
|
+
if (value === undefined)
|
|
87
|
+
return undefined;
|
|
88
|
+
if (!Array.isArray(value) || !value.every(isContextFileScope))
|
|
89
|
+
fail("INVALID_METADATA", `${rolePath}.contextFiles must be an array containing only global, project, or cwd`);
|
|
90
|
+
return [...value];
|
|
91
|
+
}
|
|
92
|
+
export function validateWorkflowExtensionSettings(value, settingsPath, errorCode = "INVALID_SETTINGS") {
|
|
93
|
+
if (value === undefined)
|
|
94
|
+
return undefined;
|
|
95
|
+
const base = `${settingsPath}.extensionSettings`;
|
|
96
|
+
if (!object(value))
|
|
97
|
+
fail(errorCode, `${base} must be an object`);
|
|
98
|
+
const normalized = {};
|
|
99
|
+
for (const [namespace, raw] of Object.entries(value)) {
|
|
100
|
+
if (!validWorkflowExtensionNamespace(namespace))
|
|
101
|
+
fail(errorCode, `${base} contains an invalid namespace: ${namespace}`);
|
|
102
|
+
if (namespace === "herdr") {
|
|
103
|
+
if (!object(raw))
|
|
104
|
+
fail(errorCode, `${base}.herdr must be an object`);
|
|
105
|
+
if (Object.keys(raw).some((key) => key !== "enableFullyInspectableMode"))
|
|
106
|
+
fail(errorCode, `${base}.herdr contains an unsupported setting`);
|
|
107
|
+
if (raw.enableFullyInspectableMode !== undefined && typeof raw.enableFullyInspectableMode !== "boolean")
|
|
108
|
+
fail(errorCode, `${base}.herdr.enableFullyInspectableMode must be a boolean`);
|
|
109
|
+
normalized.herdr = Object.freeze({ ...(raw.enableFullyInspectableMode === undefined ? {} : { enableFullyInspectableMode: raw.enableFullyInspectableMode }) });
|
|
110
|
+
continue;
|
|
111
|
+
}
|
|
112
|
+
if (namespace === "trajectory") {
|
|
113
|
+
if (!object(raw))
|
|
114
|
+
fail(errorCode, `${base}.trajectory must be an object`);
|
|
115
|
+
if (Object.keys(raw).some((key) => key !== "port"))
|
|
116
|
+
fail(errorCode, `${base}.trajectory contains an unsupported setting`);
|
|
117
|
+
if (raw.port !== undefined && (!positiveInteger(raw.port) || raw.port > 65535))
|
|
118
|
+
fail(errorCode, `${base}.trajectory.port must be an integer from 1 to 65535`);
|
|
119
|
+
normalized.trajectory = Object.freeze({ ...(raw.port === undefined ? {} : { port: raw.port }) });
|
|
120
|
+
continue;
|
|
121
|
+
}
|
|
122
|
+
if (!jsonValue(raw))
|
|
123
|
+
fail(errorCode, `${base}.${namespace} must be JSON-compatible`);
|
|
124
|
+
normalized[namespace] = structuredClone(raw);
|
|
125
|
+
}
|
|
126
|
+
return deepFreeze(normalized);
|
|
127
|
+
}
|
|
128
|
+
function positiveRetentionInteger(value) { return positiveInteger(value) && Number.isSafeInteger(value); }
|
|
129
|
+
function validateRetention(value, settingsPath) {
|
|
130
|
+
if (value === undefined)
|
|
131
|
+
return undefined;
|
|
132
|
+
const base = `${settingsPath}.retention`;
|
|
133
|
+
if (!object(value))
|
|
134
|
+
fail("INVALID_SETTINGS", `${base} must be an object`);
|
|
135
|
+
const unknown = Object.keys(value).find((key) => key !== "olderThanDays" && key !== "maxTerminalRuns");
|
|
136
|
+
if (unknown)
|
|
137
|
+
fail("INVALID_SETTINGS", `Unknown retention setting at ${base}: ${unknown}`);
|
|
138
|
+
if (value.olderThanDays !== undefined && !positiveRetentionInteger(value.olderThanDays))
|
|
139
|
+
fail("INVALID_SETTINGS", `${base}.olderThanDays must be a positive integer`);
|
|
140
|
+
if (value.maxTerminalRuns !== undefined && !positiveRetentionInteger(value.maxTerminalRuns))
|
|
141
|
+
fail("INVALID_SETTINGS", `${base}.maxTerminalRuns must be a positive integer`);
|
|
142
|
+
return Object.freeze({ ...(value.olderThanDays === undefined ? {} : { olderThanDays: value.olderThanDays }), ...(value.maxTerminalRuns === undefined ? {} : { maxTerminalRuns: value.maxTerminalRuns }) });
|
|
143
|
+
}
|
|
144
|
+
function parseSettings(path, partial) {
|
|
145
|
+
let parsed;
|
|
146
|
+
try {
|
|
147
|
+
parsed = JSON.parse(readFileSync(path, "utf8"));
|
|
148
|
+
}
|
|
149
|
+
catch (error) {
|
|
150
|
+
if (isNodeError(error, "ENOENT"))
|
|
151
|
+
return partial ? Object.freeze({}) : DEFAULT_SETTINGS;
|
|
152
|
+
fail("CONFIG_ERROR", `Invalid workflow settings JSON at ${path}: ${errorText(error)}`);
|
|
153
|
+
}
|
|
154
|
+
if (!object(parsed))
|
|
155
|
+
fail("INVALID_SETTINGS", `Workflow settings at ${path} must be an object`);
|
|
156
|
+
const allowed = new Set(["concurrency", "modelAliases", "skills", "extensions", "extensionSettings", "tools", "retention", "worktreePostCreateCommand", ...(partial ? [] : ["backgroundWidget", "codemodeTools"])]);
|
|
157
|
+
const unknown = Object.keys(parsed).find((key) => !allowed.has(key));
|
|
158
|
+
if (Object.prototype.hasOwnProperty.call(parsed, "disabledAgentResources"))
|
|
159
|
+
fail("INVALID_SETTINGS", `disabledAgentResources is no longer supported; use skills, extensions, and tools selectors (settings: ${path})`);
|
|
160
|
+
if (unknown)
|
|
161
|
+
fail("INVALID_SETTINGS", `Unknown workflow setting at ${path}: ${unknown}`);
|
|
162
|
+
const concurrency = parsed.concurrency === undefined ? (partial ? undefined : DEFAULT_SETTINGS.concurrency) : parsed.concurrency;
|
|
163
|
+
if (concurrency !== undefined && (!positiveInteger(concurrency) || concurrency > 16))
|
|
164
|
+
fail("INVALID_SETTINGS", `${path}.concurrency must be an integer from 1 to 16`);
|
|
165
|
+
const backgroundWidget = parsed.backgroundWidget === undefined ? (partial ? undefined : DEFAULT_SETTINGS.backgroundWidget) : parsed.backgroundWidget;
|
|
166
|
+
if (backgroundWidget !== undefined && typeof backgroundWidget !== "boolean")
|
|
167
|
+
fail("INVALID_SETTINGS", `${path}.backgroundWidget must be a boolean`);
|
|
168
|
+
const codemodeTools = parsed.codemodeTools;
|
|
169
|
+
if (codemodeTools !== undefined && codemodeTools !== "all" && codemodeTools !== "read-only" && codemodeTools !== "none")
|
|
170
|
+
fail("INVALID_SETTINGS", `${path}.codemodeTools must be "all", "read-only", or "none"`);
|
|
171
|
+
const modelAliases = parsed.modelAliases === undefined ? undefined : validateModelAliases(parsed.modelAliases, path);
|
|
172
|
+
const skills = validateSelectorList(parsed.skills, path, "skills");
|
|
173
|
+
const tools = validateSelectorList(parsed.tools, path, "tools");
|
|
174
|
+
const extensions = validateSelectorList(parsed.extensions, path, "extensions");
|
|
175
|
+
const extensionSettings = parsed.extensionSettings === undefined ? undefined : validateWorkflowExtensionSettings(parsed.extensionSettings, path, "INVALID_SETTINGS");
|
|
176
|
+
const retention = validateRetention(parsed.retention, path);
|
|
177
|
+
const worktreePostCreateCommand = validateWorktreePostCreateCommand(parsed.worktreePostCreateCommand, path);
|
|
178
|
+
return Object.freeze({
|
|
179
|
+
...(concurrency === undefined ? {} : { concurrency }), ...(backgroundWidget === undefined ? {} : { backgroundWidget }), ...(codemodeTools === undefined ? {} : { codemodeTools }), ...(modelAliases === undefined ? {} : { modelAliases }),
|
|
180
|
+
...(skills === undefined ? {} : { skills }), ...(extensions === undefined ? {} : { extensions }),
|
|
181
|
+
...(extensionSettings === undefined ? {} : { extensionSettings }), ...(tools === undefined ? {} : { tools }), ...(retention === undefined ? {} : { retention }),
|
|
182
|
+
...(worktreePostCreateCommand === undefined ? {} : { worktreePostCreateCommand }),
|
|
183
|
+
});
|
|
184
|
+
}
|
|
185
|
+
export function loadSettings(path = workflowSettingsPath()) { return parseSettings(path, false); }
|
|
186
|
+
const READ_ONLY_WORKFLOW_TOOLS = new Set(["workflow_status", "workflow_catalog", "subagents_inspect"]);
|
|
187
|
+
/**
|
|
188
|
+
* Exposure and annotations of a workflow or subagent tool. Under the global `codemodeTools` setting,
|
|
189
|
+
* `model-only` keeps the model's direct access and stops codemode scripts from calling the tool.
|
|
190
|
+
* Tools are registered once at load, so the setting is global and applies after a reload.
|
|
191
|
+
* Read-only tools carry `readOnlyHint` so permission extensions need not confirm them.
|
|
192
|
+
*/
|
|
193
|
+
export function workflowToolExposure(name, codemodeTools) {
|
|
194
|
+
const readOnly = READ_ONLY_WORKFLOW_TOOLS.has(name);
|
|
195
|
+
return { ...(codemodeTools === "none" || (codemodeTools === "read-only" && !readOnly) ? { exposure: "model-only" } : {}), ...(readOnly ? { annotations: { readOnlyHint: true } } : {}) };
|
|
196
|
+
}
|
|
197
|
+
/** The `codemodeTools` setting at load time. Invalid settings keep the default; launches report them. */
|
|
198
|
+
export function loadCodemodeToolsSetting(agentDir) {
|
|
199
|
+
try {
|
|
200
|
+
return loadSettings(workflowSettingsPath(agentDir)).codemodeTools;
|
|
201
|
+
}
|
|
202
|
+
catch {
|
|
203
|
+
return undefined;
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
export function loadSettingsOverrides(path) { return parseSettings(path, true); }
|
|
207
|
+
export function resolveWorkflowSettings(cwd, projectTrusted, globalSettingsPath = workflowSettingsPath()) {
|
|
208
|
+
const projectSettingsPath = workflowProjectSettingsPath(cwd);
|
|
209
|
+
const global = loadSettings(globalSettingsPath);
|
|
210
|
+
const project = projectTrusted ? loadSettingsOverrides(projectSettingsPath) : Object.freeze({});
|
|
211
|
+
const projectHas = (key) => Object.prototype.hasOwnProperty.call(project, key);
|
|
212
|
+
const sourceFor = (key) => projectHas(key) ? projectSettingsPath : globalSettingsPath;
|
|
213
|
+
const globalSelectors = selectorsFromSettings(global);
|
|
214
|
+
const projectSelectors = selectorsFromSettings(project);
|
|
215
|
+
const effectiveSelectors = selectorSet({
|
|
216
|
+
skills: [...(globalSelectors.skills ?? []), ...(projectSelectors.skills ?? [])],
|
|
217
|
+
extensions: [...(globalSelectors.extensions ?? []), ...(projectSelectors.extensions ?? [])],
|
|
218
|
+
...(globalSelectors.tools === undefined && projectSelectors.tools === undefined ? {} : { tools: [...(globalSelectors.tools ?? []), ...(projectSelectors.tools ?? [])] }),
|
|
219
|
+
});
|
|
220
|
+
const hasExtensionSelectors = global.extensions !== undefined || project.extensions !== undefined;
|
|
221
|
+
const extensionSettings = projectHas("extensionSettings") ? project.extensionSettings : global.extensionSettings;
|
|
222
|
+
const sources = {
|
|
223
|
+
concurrency: projectHas("concurrency") ? projectSettingsPath : globalSettingsPath,
|
|
224
|
+
modelAliases: projectHas("modelAliases") ? projectSettingsPath : globalSettingsPath,
|
|
225
|
+
skills: sourceFor("skills"), extensions: sourceFor("extensions"), tools: sourceFor("tools"),
|
|
226
|
+
...(extensionSettings === undefined ? {} : { extensionSettings: projectHas("extensionSettings") ? projectSettingsPath : globalSettingsPath }),
|
|
227
|
+
...(project.retention === undefined && global.retention === undefined ? {} : { retention: project.retention === undefined ? globalSettingsPath : projectSettingsPath }),
|
|
228
|
+
...(projectHas("worktreePostCreateCommand") || global.worktreePostCreateCommand !== undefined ? { worktreePostCreateCommand: projectHas("worktreePostCreateCommand") ? projectSettingsPath : globalSettingsPath } : {}),
|
|
229
|
+
};
|
|
230
|
+
const effective = Object.freeze({
|
|
231
|
+
concurrency: project.concurrency ?? global.concurrency,
|
|
232
|
+
backgroundWidget: global.backgroundWidget ?? true,
|
|
233
|
+
...(projectHas("modelAliases") ? { modelAliases: project.modelAliases } : global.modelAliases === undefined ? {} : { modelAliases: global.modelAliases }),
|
|
234
|
+
...(effectiveSelectors.skills.length ? { skills: effectiveSelectors.skills } : global.skills !== undefined || project.skills !== undefined ? { skills: effectiveSelectors.skills } : {}),
|
|
235
|
+
...(hasExtensionSelectors ? { extensions: effectiveSelectors.extensions } : {}),
|
|
236
|
+
...(extensionSettings === undefined ? {} : { extensionSettings }),
|
|
237
|
+
...(effectiveSelectors.tools?.length ? { tools: effectiveSelectors.tools } : global.tools !== undefined || project.tools !== undefined ? { tools: effectiveSelectors.tools } : {}),
|
|
238
|
+
...((project.retention ?? global.retention) === undefined ? {} : { retention: project.retention ?? global.retention }),
|
|
239
|
+
...(projectHas("worktreePostCreateCommand") ? { worktreePostCreateCommand: project.worktreePostCreateCommand } : global.worktreePostCreateCommand === undefined ? {} : { worktreePostCreateCommand: global.worktreePostCreateCommand }),
|
|
240
|
+
});
|
|
241
|
+
return { globalSettingsPath, projectSettingsPath, projectTrusted, global, project, effective, sources };
|
|
242
|
+
}
|
|
243
|
+
export function validateModelAliasAvailability(aliases, names, availableModels, knownModels, settingsPath) {
|
|
244
|
+
for (const name of names) {
|
|
245
|
+
try {
|
|
246
|
+
const target = modelCapability(name, aliases, knownModels, settingsPath);
|
|
247
|
+
if (!availableModels.has(target))
|
|
248
|
+
unknownModel(name, target, settingsPath);
|
|
249
|
+
}
|
|
250
|
+
catch (error) {
|
|
251
|
+
throw annotateModelAliasError(error, name);
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
export function resolveAgentResourcePolicy(cwd, projectTrusted, globalSettingsPath = workflowSettingsPath()) {
|
|
256
|
+
const resolved = resolveWorkflowSettings(cwd, projectTrusted, globalSettingsPath);
|
|
257
|
+
const global = selectorSet(selectorsFromSettings(resolved.global));
|
|
258
|
+
const project = selectorSet(selectorsFromSettings(resolved.project));
|
|
259
|
+
const effective = selectorSet(selectorsFromSettings(resolved.effective));
|
|
260
|
+
return { globalSettingsPath: resolved.globalSettingsPath, projectSettingsPath: resolved.projectSettingsPath, projectTrusted, global, project, effective, unmatchedSkills: [], unmatchedExtensions: [], unmatchedTools: [], selectorSources: { global: selectorsFromSettings(resolved.global), project: selectorsFromSettings(resolved.project) } };
|
|
261
|
+
}
|
|
262
|
+
export function saveModelAliases(path = workflowSettingsPath(), aliases = {}) {
|
|
263
|
+
const normalized = validateModelAliases(aliases, path);
|
|
264
|
+
let parsed = {};
|
|
265
|
+
try {
|
|
266
|
+
loadSettings(path);
|
|
267
|
+
parsed = JSON.parse(readFileSync(path, "utf8"));
|
|
268
|
+
}
|
|
269
|
+
catch (error) {
|
|
270
|
+
if (!isNodeError(error, "ENOENT"))
|
|
271
|
+
throw error;
|
|
272
|
+
}
|
|
273
|
+
if (!object(parsed))
|
|
274
|
+
fail("INVALID_SETTINGS", `Workflow settings at ${path} must be an object`);
|
|
275
|
+
mkdirSync(dirname(path), { recursive: true });
|
|
276
|
+
atomicWriteFile(path, `${JSON.stringify({ ...parsed, modelAliases: normalized }, null, 2)}\n`, true);
|
|
277
|
+
}
|
package/dist/src/trajectory.d.ts
CHANGED
|
@@ -1,10 +1,43 @@
|
|
|
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, WorkflowErrorCode, WorkflowScriptCall } 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 TrajectoryScriptCall = WorkflowScriptCall & {
|
|
28
|
+
readonly output: TrajectoryAgentOutput | {
|
|
29
|
+
readonly status: "completed";
|
|
30
|
+
} | {
|
|
31
|
+
readonly status: "failed" | "cancelled";
|
|
32
|
+
readonly code: WorkflowErrorCode;
|
|
33
|
+
};
|
|
34
|
+
};
|
|
35
|
+
export type TrajectoryRunRecord = Omit<PersistedRun, "agents" | "scriptCalls"> & {
|
|
36
|
+
readonly agents: readonly TrajectoryAgent[];
|
|
37
|
+
readonly scriptCalls?: readonly TrajectoryScriptCall[];
|
|
38
|
+
};
|
|
6
39
|
export type TrajectoryRun = {
|
|
7
|
-
run:
|
|
40
|
+
run: TrajectoryRunRecord;
|
|
8
41
|
snapshot: Readonly<LaunchSnapshot>;
|
|
9
42
|
awaiting: readonly AwaitingCheckpoint[];
|
|
10
43
|
createdAt?: string;
|
|
@@ -58,6 +91,7 @@ export type TrajectorySubagent = {
|
|
|
58
91
|
readonly code: string;
|
|
59
92
|
readonly message: string;
|
|
60
93
|
} | TrajectorySubagentArtifact;
|
|
94
|
+
output: TrajectoryAgentOutput;
|
|
61
95
|
transcript: readonly unknown[];
|
|
62
96
|
};
|
|
63
97
|
export type TrajectorySubagentMetadata = Omit<TrajectorySubagent, "transcript"> & {
|
|
@@ -104,13 +138,13 @@ export type TrajectoryPublisherInput = {
|
|
|
104
138
|
cwd: string;
|
|
105
139
|
sessionId: string;
|
|
106
140
|
port?: number;
|
|
107
|
-
themes: boolean;
|
|
108
141
|
loadRuns: TrajectoryRunLoader;
|
|
109
142
|
loadSubagents: TrajectorySubagentLoader;
|
|
110
143
|
loadMetadata: TrajectoryPublisherMetadataLoader;
|
|
111
144
|
loadTranscript?: TrajectoryTranscriptLoader;
|
|
112
145
|
handleAction: TrajectoryActionHandler;
|
|
113
146
|
};
|
|
147
|
+
export declare function isTimingTranscriptEntry(value: unknown): boolean;
|
|
114
148
|
export declare function applySystemPrompts(run: PersistedRun, prompts: readonly EffectiveSystemPrompt[]): PersistedRun;
|
|
115
149
|
export declare function applyToolDescriptions(run: PersistedRun, catalog: ReadonlyMap<string, string>): PersistedRun;
|
|
116
150
|
export declare function withPiToolDescriptions(run: PersistedRun): PersistedRun;
|
|
@@ -120,6 +154,7 @@ export declare function withPiToolDescriptionsForTools(tools: readonly string[],
|
|
|
120
154
|
}[];
|
|
121
155
|
export declare function withResolvedAttemptResources(attempt: AgentAttemptSummary, cwd: string): Promise<AgentAttemptSummary>;
|
|
122
156
|
export declare function withResolvedResources(run: PersistedRun, cwd: string): Promise<PersistedRun>;
|
|
157
|
+
export declare function applyTrajectoryAgentOutputs(run: PersistedRun, operations: readonly CompletedOperation[]): TrajectoryRunRecord;
|
|
123
158
|
export declare function loadTrajectoryRuns(cwd: string, sessionId: string, home?: string, overlay?: (run: PersistedRun) => PersistedRun): Promise<readonly TrajectoryRun[]>;
|
|
124
159
|
export declare function createTrajectoryRunLoader(cwd: string, sessionId: string, home?: string, overlay?: (run: PersistedRun) => PersistedRun): TrajectoryRunLoader;
|
|
125
160
|
export declare function createTrajectoryRunMetadataLoader(cwd: string, sessionId: string, home?: string, overlay?: (run: PersistedRun) => PersistedRun): TrajectoryRunMetadataLoader;
|
package/dist/src/trajectory.js
CHANGED
|
@@ -9,8 +9,26 @@ import { normalizeSubagentRunRequest } from "../subagents/src/contracts.js";
|
|
|
9
9
|
import { statusValue, subagentErrorValue } from "../subagents/src/decode.js";
|
|
10
10
|
import { isNodeError, jsonValue, object, resourcePatternHasMagic, selectResourcesByLayers } from "./utils.js";
|
|
11
11
|
import { canonicalPath, sameFilesystemPath } from "./paths.js";
|
|
12
|
+
import { TOOL_TIMING_ENTRY_TYPE } from "./tool-timing.js";
|
|
12
13
|
export const TRAJECTORY_MAX_TRANSCRIPT_BYTES = 2 * 1024 * 1024;
|
|
13
14
|
const TRAJECTORY_MAX_NON_TIMING_ENTRIES = 400;
|
|
15
|
+
function subagentOutput(options) {
|
|
16
|
+
if (options.resultTruncated)
|
|
17
|
+
return { status: "truncated", kind: "result", path: options.resultPath, bytes: options.resultBytes ?? 0 };
|
|
18
|
+
if (options.result !== undefined)
|
|
19
|
+
return { status: "available", value: options.result, bytes: serializedJsonBytes(options.result) };
|
|
20
|
+
if (options.failureTruncated)
|
|
21
|
+
return { status: "truncated", kind: "failure", path: options.failurePath, bytes: options.failureBytes ?? 0 };
|
|
22
|
+
if (options.failure !== undefined)
|
|
23
|
+
return { status: "failed", code: options.failure.code, message: options.failure.message };
|
|
24
|
+
if (options.state === "failed" || options.state === "stopped") {
|
|
25
|
+
const error = options.statusError ?? options.attempt?.error;
|
|
26
|
+
return { status: options.state === "stopped" ? "cancelled" : "failed", code: error?.code ?? (options.state === "stopped" ? "CANCELLED" : "AGENT_FAILED"), message: error?.message ?? `Subagent ${options.state}` };
|
|
27
|
+
}
|
|
28
|
+
if (options.state === "completed")
|
|
29
|
+
return { status: "unavailable" };
|
|
30
|
+
return { status: "pending" };
|
|
31
|
+
}
|
|
14
32
|
export { isTrajectoryAction, isTrajectoryTarget, trajectoryActionError } from "./trajectory-contracts.js";
|
|
15
33
|
function sessionFile(reference) {
|
|
16
34
|
const locator = reference?.locator;
|
|
@@ -33,7 +51,7 @@ function transcriptToolCallId(value) {
|
|
|
33
51
|
return part.id;
|
|
34
52
|
return undefined;
|
|
35
53
|
}
|
|
36
|
-
function isTimingTranscriptEntry(value) { return object(value) && value.type === "custom" && value.customType ===
|
|
54
|
+
export function isTimingTranscriptEntry(value) { return object(value) && value.type === "custom" && value.customType === TOOL_TIMING_ENTRY_TYPE; }
|
|
37
55
|
function timingToolCallId(value) {
|
|
38
56
|
if (!object(value) || !isTimingTranscriptEntry(value) || !object(value.data))
|
|
39
57
|
return undefined;
|
|
@@ -177,7 +195,8 @@ export function withPiToolDescriptionsForTools(tools, cwd) {
|
|
|
177
195
|
function canonicalExtensionSelector(selector, base) {
|
|
178
196
|
const negated = selector.startsWith("!");
|
|
179
197
|
const body = negated ? selector.slice(1) : selector;
|
|
180
|
-
|
|
198
|
+
// Built-in extensions are named `builtin:<name>`, not by a path.
|
|
199
|
+
if (body === "*" || body === "**" || body.startsWith("**/") || body.startsWith("builtin:"))
|
|
181
200
|
return selector;
|
|
182
201
|
const resolved = resolve(base, body);
|
|
183
202
|
if (resourcePatternHasMagic(body))
|
|
@@ -254,11 +273,68 @@ function withoutAgentActivities(run) {
|
|
|
254
273
|
});
|
|
255
274
|
return agents.every((agent, index) => agent === run.agents[index]) ? run : { ...run, agents };
|
|
256
275
|
}
|
|
276
|
+
function serializedJsonBytes(value) {
|
|
277
|
+
const serialized = JSON.stringify(value);
|
|
278
|
+
return Buffer.byteLength(serialized, "utf8");
|
|
279
|
+
}
|
|
280
|
+
function failedAgentOutput(agent) {
|
|
281
|
+
const error = [...(agent.attemptDetails ?? [])].reverse().find((attempt) => attempt.error)?.error;
|
|
282
|
+
const status = agent.state === "cancelled" ? "cancelled" : "failed";
|
|
283
|
+
return { status, code: error?.code ?? (status === "cancelled" ? "CANCELLED" : "AGENT_FAILED"), message: error?.message ?? `Agent ${agent.state}` };
|
|
284
|
+
}
|
|
285
|
+
function operationOutput(operation) {
|
|
286
|
+
const bytes = serializedJsonBytes(operation.value);
|
|
287
|
+
return bytes > DEFAULT_MAX_BYTES ? { status: "truncated", kind: "result", bytes } : { status: "available", value: operation.value, bytes };
|
|
288
|
+
}
|
|
289
|
+
function outputForCall(call, operation) {
|
|
290
|
+
if (operation !== undefined)
|
|
291
|
+
return call.kind === "tool" ? { status: "completed" } : operationOutput(operation);
|
|
292
|
+
if (call.error)
|
|
293
|
+
return { status: call.error.code === "CANCELLED" ? "cancelled" : "failed", code: call.error.code, ...(call.kind === "shell" ? { message: call.error.message ?? "Shell failed" } : {}) };
|
|
294
|
+
return call.finishedAt === undefined ? { status: "pending" } : { status: "unavailable" };
|
|
295
|
+
}
|
|
296
|
+
function outputForAgent(agent, operation) {
|
|
297
|
+
if (operation !== undefined)
|
|
298
|
+
return operationOutput(operation);
|
|
299
|
+
if (agent.state === "failed" || agent.state === "cancelled")
|
|
300
|
+
return failedAgentOutput(agent);
|
|
301
|
+
if (agent.state === "completed")
|
|
302
|
+
return { status: "unavailable" };
|
|
303
|
+
return { status: "pending" };
|
|
304
|
+
}
|
|
305
|
+
export function applyTrajectoryAgentOutputs(run, operations) {
|
|
306
|
+
const byPath = new Map(operations.map((operation) => [operation.path, operation]));
|
|
307
|
+
const agents = run.agents.map((agent) => ({ ...agent, output: outputForAgent(agent, agent.resultPath === undefined ? undefined : byPath.get(agent.resultPath)) }));
|
|
308
|
+
const scriptCalls = run.scriptCalls?.map((call) => {
|
|
309
|
+
const recorded = { ...call, output: outputForCall(call, byPath.get(call.path)) };
|
|
310
|
+
if (call.kind === "tool") {
|
|
311
|
+
delete recorded.input;
|
|
312
|
+
delete recorded.inputBytes;
|
|
313
|
+
if (call.error)
|
|
314
|
+
recorded.error = { code: call.error.code };
|
|
315
|
+
}
|
|
316
|
+
return recorded;
|
|
317
|
+
});
|
|
318
|
+
const rest = run;
|
|
319
|
+
return { ...rest, agents, ...(scriptCalls === undefined ? {} : { scriptCalls }) };
|
|
320
|
+
}
|
|
321
|
+
function overlayTrajectoryRun(run, overlay) {
|
|
322
|
+
const overlaid = overlay(run);
|
|
323
|
+
const outputs = new Map(run.agents.map((agent) => [agent.id, agent.output]));
|
|
324
|
+
const agents = overlaid.agents.map((agent) => {
|
|
325
|
+
const previous = outputs.get(agent.id);
|
|
326
|
+
const output = previous?.status === "available" || previous?.status === "truncated" ? previous : outputForAgent(agent, undefined);
|
|
327
|
+
return { ...agent, output };
|
|
328
|
+
});
|
|
329
|
+
return { ...overlaid, agents, ...(run.scriptCalls === undefined ? {} : { scriptCalls: run.scriptCalls }) };
|
|
330
|
+
}
|
|
257
331
|
async function loadTrajectoryRun(store, includeTranscripts = true) {
|
|
258
332
|
const value = await store.load();
|
|
259
333
|
const summary = await store.loadSummary().catch(() => undefined);
|
|
260
334
|
const prompts = await store.systemPrompts().catch(() => []);
|
|
261
|
-
const
|
|
335
|
+
const prepared = withoutAgentActivities(await withResolvedResources(withPiToolDescriptions(applySystemPrompts(value.run, prompts)), store.cwd));
|
|
336
|
+
const operations = await store.replayableOperations().catch(() => []);
|
|
337
|
+
const run = applyTrajectoryAgentOutputs(prepared, operations);
|
|
262
338
|
return { run, snapshot: value.snapshot, awaiting: await store.awaitingCheckpoints(), ...(summary?.createdAt === undefined ? {} : { createdAt: summary.createdAt }), transcripts: includeTranscripts ? await runTranscripts(run) : {} };
|
|
263
339
|
}
|
|
264
340
|
export async function loadTrajectoryRuns(cwd, sessionId, home = homedir(), overlay) {
|
|
@@ -266,7 +342,7 @@ export async function loadTrajectoryRuns(cwd, sessionId, home = homedir(), overl
|
|
|
266
342
|
for (const runId of await listRunIds(cwd, sessionId, home, false)) {
|
|
267
343
|
try {
|
|
268
344
|
const value = await loadTrajectoryRun(new RunStore(cwd, sessionId, runId, home));
|
|
269
|
-
loaded.push(overlay ? { ...value, run:
|
|
345
|
+
loaded.push(overlay ? { ...value, run: overlayTrajectoryRun(value.run, overlay) } : value);
|
|
270
346
|
}
|
|
271
347
|
catch { /* Ignore corrupt or concurrently removed runs. */ }
|
|
272
348
|
}
|
|
@@ -305,7 +381,7 @@ function createCachedRunLoader(cwd, sessionId, home, load, transcriptSignature,
|
|
|
305
381
|
entry = { value, stateMtimeMs: (await fileMtime(join(store.directory, "state.json"))) ?? 0, journalMtimeMs: (await fileMtime(join(store.directory, "journal.json"))) ?? 0, transcripts };
|
|
306
382
|
cache.set(runId, entry);
|
|
307
383
|
}
|
|
308
|
-
loaded.push(overlay ? { ...entry.value, run:
|
|
384
|
+
loaded.push(overlay ? { ...entry.value, run: overlayTrajectoryRun(entry.value.run, overlay) } : entry.value);
|
|
309
385
|
}
|
|
310
386
|
catch {
|
|
311
387
|
cache.delete(runId); /* Ignore corrupt or concurrently removed runs. */
|
|
@@ -513,6 +589,7 @@ async function loadTrajectorySubagent(directory, cwd, sessionId, includeTranscri
|
|
|
513
589
|
...(attempt === undefined ? {} : { attempt }),
|
|
514
590
|
...(result === undefined ? {} : { result }),
|
|
515
591
|
...(failure === undefined ? {} : { failure }),
|
|
592
|
+
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
593
|
};
|
|
517
594
|
}
|
|
518
595
|
catch {
|
package/dist/src/types.d.ts
CHANGED
|
@@ -23,7 +23,7 @@ export declare const WORKFLOW_CHECKPOINT_STATE_CHANGED_EVENT = "workflow:checkpo
|
|
|
23
23
|
export declare const WORKFLOW_BUDGET_EVENT = "workflow:budget-event";
|
|
24
24
|
export declare const WORKFLOW_WORKTREE_CREATED_EVENT = "workflow:worktree-created";
|
|
25
25
|
export declare const WORKFLOW_BLOCKED_EVENT = "workflow:blocked";
|
|
26
|
-
export declare const ERROR_CODES: readonly ["CONFIG_ERROR", "INVALID_SETTINGS", "INVALID_SYNTAX", "INVALID_METADATA", "DUPLICATE_NAME", "INVALID_SCHEMA", "UNKNOWN_MODEL", "UNKNOWN_TOOL", "UNKNOWN_AGENT_TYPE", "RUN_OWNED", "RUN_NOT_FOUND", "REGISTRY_FROZEN", "GLOBAL_COLLISION", "MISSING_WORKFLOW", "RPC_LIMIT_EXCEEDED", "SHELL_FAILED", "AGENT_TIMEOUT", "AGENT_FAILED", "AGENT_RESULT_COLLECTED", "RESULT_INVALID", "CANCELLED", "WORKER_UNRESPONSIVE", "WORKTREE_FAILED", "RESUME_INCOMPATIBLE", "BUDGET_EXHAUSTED", "INTERNAL_ERROR"];
|
|
26
|
+
export declare const ERROR_CODES: readonly ["CONFIG_ERROR", "INVALID_SETTINGS", "INVALID_SYNTAX", "INVALID_METADATA", "DUPLICATE_NAME", "INVALID_SCHEMA", "UNKNOWN_MODEL", "UNKNOWN_TOOL", "UNKNOWN_AGENT_TYPE", "RUN_OWNED", "RUN_NOT_FOUND", "REGISTRY_FROZEN", "GLOBAL_COLLISION", "MISSING_WORKFLOW", "RPC_LIMIT_EXCEEDED", "SHELL_FAILED", "AGENT_TIMEOUT", "AGENT_FAILED", "AGENT_RESULT_COLLECTED", "RESULT_INVALID", "TOOL_FAILED", "CANCELLED", "WORKER_UNRESPONSIVE", "WORKTREE_FAILED", "RESUME_INCOMPATIBLE", "BUDGET_EXHAUSTED", "INTERNAL_ERROR"];
|
|
27
27
|
export declare const THINKING_LEVELS: readonly ["off", "minimal", "low", "medium", "high", "xhigh", "max"];
|
|
28
28
|
export type ThinkingLevel = (typeof THINKING_LEVELS)[number];
|
|
29
29
|
export type WorkflowErrorCode = (typeof ERROR_CODES)[number];
|
|
@@ -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;
|
|
@@ -216,9 +215,11 @@ export interface WorkflowRetentionSettings {
|
|
|
216
215
|
olderThanDays?: number;
|
|
217
216
|
maxTerminalRuns?: number;
|
|
218
217
|
}
|
|
218
|
+
export type CodemodeToolsSetting = "all" | "read-only" | "none";
|
|
219
219
|
export interface WorkflowSettings {
|
|
220
220
|
concurrency: number;
|
|
221
221
|
backgroundWidget?: boolean;
|
|
222
|
+
codemodeTools?: CodemodeToolsSetting;
|
|
222
223
|
modelAliases?: Readonly<Record<string, string>>;
|
|
223
224
|
skills?: readonly string[];
|
|
224
225
|
extensions?: readonly string[];
|
|
@@ -227,7 +228,7 @@ export interface WorkflowSettings {
|
|
|
227
228
|
retention?: Readonly<WorkflowRetentionSettings>;
|
|
228
229
|
worktreePostCreateCommand?: readonly string[];
|
|
229
230
|
}
|
|
230
|
-
export type WorkflowSettingsOverrides = Partial<Omit<WorkflowSettings, "backgroundWidget">>;
|
|
231
|
+
export type WorkflowSettingsOverrides = Partial<Omit<WorkflowSettings, "backgroundWidget" | "codemodeTools">>;
|
|
231
232
|
export interface WorkflowSettingsSources {
|
|
232
233
|
concurrency: string;
|
|
233
234
|
modelAliases: string;
|
|
@@ -404,6 +405,21 @@ export interface WorkflowPhaseShellActivity {
|
|
|
404
405
|
active: number;
|
|
405
406
|
startedAt: number;
|
|
406
407
|
}
|
|
408
|
+
/** A script call recorded for inspection. Tool payloads stay in the replay journal, not this record. Oversized shell inputs are JSON prefixes, with `inputBytes` their full size. */
|
|
409
|
+
export interface WorkflowScriptCall {
|
|
410
|
+
kind: "shell" | "tool";
|
|
411
|
+
name: string;
|
|
412
|
+
path: string;
|
|
413
|
+
input?: JsonValue;
|
|
414
|
+
inputBytes?: number;
|
|
415
|
+
phase?: string;
|
|
416
|
+
startedAt: number;
|
|
417
|
+
finishedAt?: number;
|
|
418
|
+
error?: {
|
|
419
|
+
code: WorkflowErrorCode;
|
|
420
|
+
message?: string;
|
|
421
|
+
};
|
|
422
|
+
}
|
|
407
423
|
export interface RunRecord {
|
|
408
424
|
id: string;
|
|
409
425
|
workflowName: string;
|
|
@@ -420,6 +436,11 @@ export interface RunRecord {
|
|
|
420
436
|
activeShells?: number;
|
|
421
437
|
activeShellStartedAt?: number;
|
|
422
438
|
activeShellsByPhase?: readonly WorkflowPhaseShellActivity[];
|
|
439
|
+
activeTools?: readonly {
|
|
440
|
+
name: string;
|
|
441
|
+
startedAt: number;
|
|
442
|
+
}[];
|
|
443
|
+
scriptCalls?: readonly WorkflowScriptCall[];
|
|
423
444
|
error?: WorkflowErrorShape;
|
|
424
445
|
failedAt?: string;
|
|
425
446
|
budget?: WorkflowBudget;
|
|
@@ -836,6 +857,11 @@ export interface ShellIdentity {
|
|
|
836
857
|
occurrence: number;
|
|
837
858
|
worktreeOwner?: string;
|
|
838
859
|
}
|
|
860
|
+
/** A script `tools.<id>(args)` call: the n-th call of that tool within one structural scope. */
|
|
861
|
+
export interface ToolIdentity {
|
|
862
|
+
structuralPath: readonly string[];
|
|
863
|
+
occurrence: number;
|
|
864
|
+
}
|
|
839
865
|
export interface WorkflowBridge {
|
|
840
866
|
agent?: (prompt: string, options: Readonly<Record<string, JsonValue>>, signal: AbortSignal, identity: AgentIdentity) => Promise<JsonValue>;
|
|
841
867
|
shell?: (command: string, options: ShellOptions, signal: AbortSignal, identity: ShellIdentity) => Promise<ShellResult>;
|
|
@@ -847,6 +873,7 @@ export interface WorkflowBridge {
|
|
|
847
873
|
}>>;
|
|
848
874
|
phase?: (name: string) => void | Promise<void>;
|
|
849
875
|
log?: (message: string) => void | Promise<void>;
|
|
876
|
+
tool?: (identifier: string, args: Readonly<Record<string, JsonValue>>, signal: AbortSignal, identity: ToolIdentity) => Promise<JsonValue>;
|
|
850
877
|
}
|
|
851
878
|
export interface WorkflowExecution {
|
|
852
879
|
result: Promise<JsonValue>;
|
package/dist/src/types.js
CHANGED
|
@@ -19,7 +19,7 @@ export const WORKFLOW_BLOCKED_EVENT = "workflow:blocked";
|
|
|
19
19
|
export const ERROR_CODES = [
|
|
20
20
|
"CONFIG_ERROR", "INVALID_SETTINGS", "INVALID_SYNTAX", "INVALID_METADATA", "DUPLICATE_NAME", "INVALID_SCHEMA", "UNKNOWN_MODEL", "UNKNOWN_TOOL", "UNKNOWN_AGENT_TYPE",
|
|
21
21
|
"RUN_OWNED", "RUN_NOT_FOUND", "REGISTRY_FROZEN", "GLOBAL_COLLISION", "MISSING_WORKFLOW", "RPC_LIMIT_EXCEEDED", "SHELL_FAILED", "AGENT_TIMEOUT", "AGENT_FAILED", "AGENT_RESULT_COLLECTED", "RESULT_INVALID",
|
|
22
|
-
"CANCELLED", "WORKER_UNRESPONSIVE", "WORKTREE_FAILED", "RESUME_INCOMPATIBLE", "BUDGET_EXHAUSTED", "INTERNAL_ERROR",
|
|
22
|
+
"TOOL_FAILED", "CANCELLED", "WORKER_UNRESPONSIVE", "WORKTREE_FAILED", "RESUME_INCOMPATIBLE", "BUDGET_EXHAUSTED", "INTERNAL_ERROR",
|
|
23
23
|
];
|
|
24
24
|
export const THINKING_LEVELS = ["off", "minimal", "low", "medium", "high", "xhigh", "max"];
|
|
25
25
|
export function roleNameOf(value) {
|
package/dist/src/utils.d.ts
CHANGED
|
@@ -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>;
|
|
@@ -6,6 +6,8 @@ export declare class SerialLane {
|
|
|
6
6
|
export declare function object(value: unknown): value is Record<string, unknown>;
|
|
7
7
|
export declare function sanitizeDisplayText(value: string): string;
|
|
8
8
|
export { object as isObject };
|
|
9
|
+
/** A short digest of a JSON value that ignores object key order. */
|
|
10
|
+
export declare function jsonDigest(value: JsonValue): string;
|
|
9
11
|
export declare function jsonValue(value: unknown, seen?: Set<object>): value is JsonValue;
|
|
10
12
|
export declare function jsonObject(value: unknown): value is Record<string, JsonValue>;
|
|
11
13
|
export declare function mergeWorkflowExtensionSettings(...layers: readonly (Readonly<WorkflowExtensionSettings> | undefined)[]): Readonly<WorkflowExtensionSettings> | undefined;
|
|
@@ -44,6 +46,23 @@ export declare function modelAliasName(value: string, aliases: Readonly<Record<s
|
|
|
44
46
|
export declare function validateModelAliases(value: unknown, settingsPath?: string): Readonly<Record<string, string>>;
|
|
45
47
|
export declare function unknownModel(value: string, target: string | undefined, settingsPath?: string): never;
|
|
46
48
|
export declare function resolveModelReference(value: string, aliases?: Readonly<Record<string, string>>, knownModels?: ReadonlySet<string>, settingsPath?: string): ModelSpec;
|
|
49
|
+
type ToolSource = {
|
|
50
|
+
getActiveTools(): string[];
|
|
51
|
+
getAllTools?(): readonly {
|
|
52
|
+
name: string;
|
|
53
|
+
exposure?: string;
|
|
54
|
+
}[];
|
|
55
|
+
};
|
|
56
|
+
/**
|
|
57
|
+
* Tools a session can reach, and so the ceiling for its agents: the declared tools plus the ones
|
|
58
|
+
* only codemode scripts call or tool_search loads, such as MCP tools with the default exposure.
|
|
59
|
+
* Agent sessions allow tools by name, so leaving these out would make them unreachable there.
|
|
60
|
+
*/
|
|
61
|
+
export declare function reachableTools(pi: ToolSource): string[];
|
|
62
|
+
/** Provider of the virtual models that expose workflow model aliases in `/model`. */
|
|
63
|
+
export declare const VIRTUAL_MODEL_PROVIDER = "workflow";
|
|
64
|
+
/** Child sessions do not load the workflow extension, so they receive the alias target instead of its virtual model. */
|
|
65
|
+
export declare function physicalModel(spec: ModelSpec, aliases?: Readonly<Record<string, string>>, knownModels?: ReadonlySet<string>, settingsPath?: string): ModelSpec;
|
|
47
66
|
export declare function modelCapability(value: string | ModelSpec, aliases?: Readonly<Record<string, string>>, knownModels?: ReadonlySet<string>, settingsPath?: string): string;
|
|
48
67
|
export declare function aliasDrift(previous: Readonly<Record<string, string>>, current: Readonly<Record<string, string>>): string[];
|
|
49
68
|
export declare function validateResourcePattern(pattern: string): void;
|
|
@@ -55,3 +74,4 @@ export declare function createLaunchSnapshot(input: Omit<import("./types.js").La
|
|
|
55
74
|
identityVersion?: number;
|
|
56
75
|
}): Readonly<import("./types.js").LaunchSnapshot>;
|
|
57
76
|
export declare function loadLaunchSnapshot(input: import("./types.js").LaunchSnapshot): Readonly<import("./types.js").LaunchSnapshot>;
|
|
77
|
+
export declare function validateSchema(schema: unknown, at?: string): asserts schema is JsonSchema;
|