@marcoscale98/pi-extensible-workflows 5.17.0-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 +68 -0
- package/dist/src/agent-execution.js +81 -39
- package/dist/src/background-widget.d.ts +5 -0
- package/dist/src/background-widget.js +1 -1
- package/dist/src/decoders.js +26 -0
- 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-recovery.d.ts +5 -2
- package/dist/src/host-recovery.js +3 -3
- package/dist/src/host-view.d.ts +5 -0
- package/dist/src/host-view.js +29 -5
- package/dist/src/host.d.ts +2 -2
- package/dist/src/host.js +190 -37
- package/dist/src/index.js +495 -93
- 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 +1 -1
- package/dist/src/roles.js +4 -3
- package/dist/src/script-tools.d.ts +27 -0
- package/dist/src/script-tools.js +87 -0
- package/dist/src/settings.d.ts +13 -0
- package/dist/src/settings.js +27 -3
- package/dist/src/trajectory.d.ts +12 -2
- package/dist/src/trajectory.js +30 -8
- package/dist/src/types.d.ts +30 -2
- package/dist/src/types.js +1 -1
- package/dist/src/utils.d.ts +19 -0
- package/dist/src/utils.js +30 -0
- package/dist/src/validation.d.ts +6 -1
- package/dist/src/validation.js +21 -1
- 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 +267 -159
- package/dist/subagents/index.js.map +2 -2
- package/dist/subagents/src/contracts.d.ts +45 -29
- package/dist/subagents/src/contracts.js +18 -7
- package/dist/subagents/src/index.d.ts +1 -1
- package/dist/subagents/src/index.js +13 -6
- package/dist/subagents/src/manager.js +3 -3
- package/dist/subagents/src/view.d.ts +1 -0
- package/dist/subagents/src/view.js +13 -52
- package/dist/trajectory/assets/index.html +27 -5
- package/dist/trajectory/index.js +76 -28
- package/dist/trajectory/index.js.map +2 -2
- package/dist/trajectory/src/assets/index.html +27 -5
- package/dist/trajectory/src/index.js +8 -18
- package/dist/trajectory/src/server.js +4 -2
- package/dist/trajectory/src/server.js.map +2 -2
- package/package.json +6 -6
- package/skills/pi-extensible-workflows/SKILL.md +2 -0
- package/src/agent-execution.ts +77 -41
- package/src/background-widget.ts +1 -1
- package/src/decoders.ts +20 -0
- package/src/execution.ts +41 -4
- package/src/host-delivery.ts +1 -0
- package/src/host-recovery.ts +8 -7
- package/src/host-view.ts +23 -5
- package/src/host.ts +173 -35
- package/src/paths.ts +3 -0
- package/src/registry.ts +1 -1
- package/src/roles.ts +4 -3
- package/src/script-tools.ts +76 -0
- package/src/settings.ts +22 -4
- package/src/trajectory.ts +30 -11
- package/src/types.ts +11 -4
- package/src/utils.ts +28 -0
- package/src/validation.ts +19 -1
- package/starter/review-loop.ts +0 -2
- package/subagents/README.md +2 -2
- package/subagents/src/contracts.ts +18 -7
- package/subagents/src/index.ts +16 -6
- package/subagents/src/manager.ts +3 -3
- package/subagents/src/view.ts +13 -50
- package/trajectory/src/assets/index.html +27 -5
- package/trajectory/src/index.ts +6 -9
- package/trajectory/src/server.ts +2 -2
package/dist/src/paths.d.ts
CHANGED
|
@@ -5,6 +5,8 @@ export declare function safePart(value: string): string;
|
|
|
5
5
|
* guessing at the identity of an object that does not exist.
|
|
6
6
|
*/
|
|
7
7
|
export declare function canonicalPath(path: string): string;
|
|
8
|
+
/** Pi names built-in extensions `builtin:<name>` instead of by a path; other extensions are files. */
|
|
9
|
+
export declare function extensionIdentity(path: string): string;
|
|
8
10
|
export declare function sameFilesystemPath(left: string, right: string): boolean;
|
|
9
11
|
export declare function workflowProjectSettingsPath(cwd: string): string;
|
|
10
12
|
export declare function projectStorageKey(cwd: string): string;
|
package/dist/src/paths.js
CHANGED
|
@@ -20,6 +20,8 @@ export function canonicalPath(path) {
|
|
|
20
20
|
return absolute;
|
|
21
21
|
}
|
|
22
22
|
}
|
|
23
|
+
/** Pi names built-in extensions `builtin:<name>` instead of by a path; other extensions are files. */
|
|
24
|
+
export function extensionIdentity(path) { return path.startsWith("builtin:") ? path : canonicalPath(path); }
|
|
23
25
|
export function sameFilesystemPath(left, right) { return canonicalPath(left) === canonicalPath(right); }
|
|
24
26
|
export function workflowProjectSettingsPath(cwd) { return join(cwd, ".pi", "pi-extensible-workflows", "settings.json"); }
|
|
25
27
|
export function projectStorageKey(cwd) {
|
package/dist/src/registry.js
CHANGED
|
@@ -7,7 +7,7 @@ import { loadSettings, resolveWorkflowSettings } from "./settings.js";
|
|
|
7
7
|
import { validateSchema } from "./utils.js";
|
|
8
8
|
import { canonicalPath } from "./paths.js";
|
|
9
9
|
import { isCorePackageName } from "./package-identity.js";
|
|
10
|
-
const RESERVED_GLOBALS = 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"]);
|
|
10
|
+
const RESERVED_GLOBALS = 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"]);
|
|
11
11
|
const IDENTIFIER = /^[A-Za-z_$][\w$]*$/;
|
|
12
12
|
const SEMVER = /^\d+\.\d+\.\d+(?:-[0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*)?(?:\+[0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*)?$/;
|
|
13
13
|
const PACKAGE_NAME = /^(?:@[a-z0-9][a-z0-9._-]*\/)?[a-z0-9][a-z0-9._-]*$/;
|
package/dist/src/roles.js
CHANGED
|
@@ -4,7 +4,7 @@ import { getAgentDir, parseFrontmatter } from "@earendil-works/pi-coding-agent";
|
|
|
4
4
|
import { registeredWorkflowRoleDirectoryRegistrations } from "./registry.js";
|
|
5
5
|
import { resolveWorkflowSettings, validateContextFileScopes, validateSelectorList, validateWorkflowExtensionSettings, workflowSettingsPath } from "./settings.js";
|
|
6
6
|
import { assertModelThinking, deepFreeze, object, errorText, fail, isNodeError, modelAliasName, modelCapability, resourcePatternHasMagic, resolveModelReference, selectResourcesByLayers, unmatchedResourcePatterns } from "./utils.js";
|
|
7
|
-
import { canonicalPath } from "./paths.js";
|
|
7
|
+
import { canonicalPath, extensionIdentity } from "./paths.js";
|
|
8
8
|
const ROLE_DIRECTORY = "pi-extensible-workflows";
|
|
9
9
|
// Preserve the role-file base when a discovered definition is resolved again through the public API.
|
|
10
10
|
const roleDefinitionPaths = new WeakMap();
|
|
@@ -227,7 +227,8 @@ export function loadAgentDefinitions(cwd, agentDir = getAgentDir(), projectTrust
|
|
|
227
227
|
export function canonicalExtensionSelector(selector, base = process.cwd()) {
|
|
228
228
|
const negated = selector.startsWith("!");
|
|
229
229
|
const body = negated ? selector.slice(1) : selector;
|
|
230
|
-
|
|
230
|
+
// Built-in extensions are named `builtin:<name>`, not by a path.
|
|
231
|
+
if (body === "*" || body === "**" || body.startsWith("**/") || body.startsWith("builtin:"))
|
|
231
232
|
return selector;
|
|
232
233
|
const resolved = resolve(base, body);
|
|
233
234
|
if (resourcePatternHasMagic(body))
|
|
@@ -296,7 +297,7 @@ export function resolveRole(name, options) {
|
|
|
296
297
|
fail("UNKNOWN_TOOL", `Tool is outside the launching session boundary: ${outsideRootTool}`);
|
|
297
298
|
}
|
|
298
299
|
const selectedSkills = resources.skills === undefined ? undefined : selectResourcesByLayers(selectorLayers.skills, resources.skills);
|
|
299
|
-
const extensionResources = resources.extensions?.map((extension) =>
|
|
300
|
+
const extensionResources = resources.extensions?.map((extension) => extensionIdentity(extension));
|
|
300
301
|
const selectedExtensions = extensionResources === undefined ? undefined : selectResourcesByLayers(selectorLayers.extensions, extensionResources);
|
|
301
302
|
const unmatchedSkills = resources.skills === undefined ? undefined : unmatchedResourcePatterns(selectorLayers.skills.flatMap((layer) => layer ?? []), resources.skills);
|
|
302
303
|
const unmatchedExtensions = extensionResources === undefined ? undefined : unmatchedResourcePatterns(selectorLayers.extensions.flatMap((layer) => layer ?? []), extensionResources);
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import type { ExtensionToolContext, ToolLoadout, ToolLoadoutChanges } from "@earendil-works/pi-coding-agent";
|
|
2
|
+
import { type JsonValue } from "./types.js";
|
|
3
|
+
type ScriptTool = ExtensionToolContext["tools"][number];
|
|
4
|
+
type ScriptToolOutcome = Awaited<ReturnType<ExtensionToolContext["executeTool"]>>;
|
|
5
|
+
/** Workflow and subagent tools stay out of scripts, so a run cannot launch or control runs. Codemode is model-only and never callable. */
|
|
6
|
+
export declare function scriptToolExcluded(name: string): boolean;
|
|
7
|
+
/** The identifier scripts use for a tool, the same as Pi codemode's, so its `tools.<id>(args)` lines apply to workflow scripts. */
|
|
8
|
+
export declare function scriptToolIdentifier(name: string): string;
|
|
9
|
+
/** The context of a Pi tool call, whose `executeTool()` runs session tools; absent for commands, session events, and headless launches. */
|
|
10
|
+
export declare function scriptToolContext(ctx: unknown): ExtensionToolContext | undefined;
|
|
11
|
+
export declare function scriptToolIdentifiers(context: ExtensionToolContext | undefined): string[];
|
|
12
|
+
export declare function scriptTool(context: ExtensionToolContext, identifier: string): ScriptTool | undefined;
|
|
13
|
+
/**
|
|
14
|
+
* What a script call resolves to, as in codemode: the structured content of a tool with an output
|
|
15
|
+
* schema, also for error results that carry one, otherwise its text; other failures throw. The value
|
|
16
|
+
* is normalized through JSON and size-checked before it is journaled, so a replay returns the same value.
|
|
17
|
+
*/
|
|
18
|
+
export declare function scriptToolValue(tool: ScriptTool, outcome: ScriptToolOutcome): JsonValue;
|
|
19
|
+
/** Fails a launch whose script names a tool this session cannot call, before any agent runs. */
|
|
20
|
+
export declare function validateScriptToolReferences(script: string, context: ExtensionToolContext | undefined): void;
|
|
21
|
+
/**
|
|
22
|
+
* Tells the model what `tools.<id>(args)` resolves to for declared tools with an output schema; the
|
|
23
|
+
* workflow description says that the others resolve to their text. With codemode active its own
|
|
24
|
+
* identical `Codemode:` lines say this, and pi keeps only one description per tool, so this defers.
|
|
25
|
+
*/
|
|
26
|
+
export declare function prepareScriptToolLoadout(loadout: ToolLoadout): ToolLoadoutChanges | undefined;
|
|
27
|
+
export {};
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import { WorkflowError } from "./types.js";
|
|
2
|
+
import { errorText, jsonValue, object } from "./utils.js";
|
|
3
|
+
import { RPC_LIMIT_BYTES } from "./execution.js";
|
|
4
|
+
import { scriptToolReferences } from "./validation.js";
|
|
5
|
+
/** Workflow and subagent tools stay out of scripts, so a run cannot launch or control runs. Codemode is model-only and never callable. */
|
|
6
|
+
export function scriptToolExcluded(name) {
|
|
7
|
+
return name === "workflow" || name.startsWith("workflow_") || name.startsWith("subagents_");
|
|
8
|
+
}
|
|
9
|
+
/** The identifier scripts use for a tool, the same as Pi codemode's, so its `tools.<id>(args)` lines apply to workflow scripts. */
|
|
10
|
+
export function scriptToolIdentifier(name) {
|
|
11
|
+
let identifier = "";
|
|
12
|
+
for (const char of name)
|
|
13
|
+
identifier += (identifier === "" ? /^[A-Za-z_$]$/ : /^[A-Za-z0-9_$]$/).test(char) ? char : "_";
|
|
14
|
+
return identifier || "_";
|
|
15
|
+
}
|
|
16
|
+
/** The context of a Pi tool call, whose `executeTool()` runs session tools; absent for commands, session events, and headless launches. */
|
|
17
|
+
export function scriptToolContext(ctx) {
|
|
18
|
+
return object(ctx) && typeof ctx.executeTool === "function" && Array.isArray(ctx.tools) ? ctx : undefined;
|
|
19
|
+
}
|
|
20
|
+
export function scriptToolIdentifiers(context) {
|
|
21
|
+
return (context?.tools ?? []).filter(({ name }) => !scriptToolExcluded(name)).map(({ name }) => scriptToolIdentifier(name));
|
|
22
|
+
}
|
|
23
|
+
// NOTE: a launch rejects identifiers that several tools share; a later tool set may still collide, and the first match wins.
|
|
24
|
+
export function scriptTool(context, identifier) {
|
|
25
|
+
return context.tools.find(({ name }) => !scriptToolExcluded(name) && scriptToolIdentifier(name) === identifier);
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* What a script call resolves to, as in codemode: the structured content of a tool with an output
|
|
29
|
+
* schema, also for error results that carry one, otherwise its text; other failures throw. The value
|
|
30
|
+
* is normalized through JSON and size-checked before it is journaled, so a replay returns the same value.
|
|
31
|
+
*/
|
|
32
|
+
export function scriptToolValue(tool, outcome) {
|
|
33
|
+
const structured = tool.outputSchema === undefined ? undefined : outcome.result.structuredContent;
|
|
34
|
+
const text = outcome.result.content.flatMap((item) => item.type === "text" ? [item.text] : []).join("\n");
|
|
35
|
+
if (structured === undefined && outcome.isError)
|
|
36
|
+
throw new WorkflowError("TOOL_FAILED", `${tool.name}: ${text || "failed"}`);
|
|
37
|
+
let value;
|
|
38
|
+
try {
|
|
39
|
+
value = JSON.parse(JSON.stringify(structured ?? text));
|
|
40
|
+
}
|
|
41
|
+
catch (error) {
|
|
42
|
+
throw new WorkflowError("TOOL_FAILED", `${tool.name} returned a result that is not JSON: ${errorText(error)}`);
|
|
43
|
+
}
|
|
44
|
+
if (!jsonValue(value))
|
|
45
|
+
throw new WorkflowError("TOOL_FAILED", `${tool.name} returned a result that is not JSON`);
|
|
46
|
+
if (Buffer.byteLength(JSON.stringify(value)) > RPC_LIMIT_BYTES)
|
|
47
|
+
throw new WorkflowError("TOOL_FAILED", `${tool.name} returned more than the 10 MB workflow RPC limit`);
|
|
48
|
+
return value;
|
|
49
|
+
}
|
|
50
|
+
/** Fails a launch whose script names a tool this session cannot call, before any agent runs. */
|
|
51
|
+
export function validateScriptToolReferences(script, context) {
|
|
52
|
+
const references = scriptToolReferences(script);
|
|
53
|
+
if (!references?.length)
|
|
54
|
+
return;
|
|
55
|
+
if (!context)
|
|
56
|
+
throw new WorkflowError("UNKNOWN_TOOL", `Workflow scripts can call session tools only when launched by the workflow tool; this launch has none for ${references.map((name) => `tools.${name}`).join(", ")}`);
|
|
57
|
+
const identifiers = scriptToolIdentifiers(context);
|
|
58
|
+
const unknown = references.filter((name) => !identifiers.includes(name));
|
|
59
|
+
if (unknown.length)
|
|
60
|
+
throw new WorkflowError("UNKNOWN_TOOL", `Workflow script calls tools this session cannot call: ${unknown.map((name) => `tools.${name}`).join(", ")}`);
|
|
61
|
+
const ambiguous = references.filter((name) => identifiers.indexOf(name) !== identifiers.lastIndexOf(name));
|
|
62
|
+
if (ambiguous.length)
|
|
63
|
+
throw new WorkflowError("UNKNOWN_TOOL", `Workflow script calls tools whose names collide after normalization: ${ambiguous.map((name) => `tools.${name}`).join(", ")}`);
|
|
64
|
+
}
|
|
65
|
+
function describeScriptOutput(schema) {
|
|
66
|
+
if (!object(schema) || schema.type !== "object" || !object(schema.properties))
|
|
67
|
+
return schema === undefined ? undefined : "a JSON value";
|
|
68
|
+
const required = new Set(Array.isArray(schema.required) ? schema.required : []);
|
|
69
|
+
return `\`{ ${Object.keys(schema.properties).map((name) => required.has(name) ? name : `${name}?`).join(", ")} }\``;
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Tells the model what `tools.<id>(args)` resolves to for declared tools with an output schema; the
|
|
73
|
+
* workflow description says that the others resolve to their text. With codemode active its own
|
|
74
|
+
* identical `Codemode:` lines say this, and pi keeps only one description per tool, so this defers.
|
|
75
|
+
*/
|
|
76
|
+
export function prepareScriptToolLoadout(loadout) {
|
|
77
|
+
if (loadout.declared.some(({ name }) => name === "codemode"))
|
|
78
|
+
return undefined;
|
|
79
|
+
const callable = new Set(loadout.callable.map(({ name }) => name));
|
|
80
|
+
const descriptions = {};
|
|
81
|
+
for (const tool of loadout.declared) {
|
|
82
|
+
const output = callable.has(tool.name) && !scriptToolExcluded(tool.name) ? describeScriptOutput(tool.outputSchema) : undefined;
|
|
83
|
+
if (output)
|
|
84
|
+
descriptions[tool.name] = `${tool.description.trim()}\n\nWorkflow scripts: \`tools.${scriptToolIdentifier(tool.name)}(args)\` resolves to ${output}.`;
|
|
85
|
+
}
|
|
86
|
+
return { descriptions };
|
|
87
|
+
}
|
package/dist/src/settings.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { type ToolAnnotations } from "@earendil-works/pi-coding-agent";
|
|
1
2
|
import type { AgentResourcePolicy, ContextFileScope, WorkflowExtensionSettings, WorkflowSettings, WorkflowSettingsOverrides, WorkflowSettingsResolution } from "./types.js";
|
|
2
3
|
export { workflowProjectSettingsPath } from "./paths.js";
|
|
3
4
|
export declare const DEFAULT_SETTINGS: Readonly<WorkflowSettings>;
|
|
@@ -6,6 +7,18 @@ export declare function validateSelectorList(value: unknown, path: string, kind:
|
|
|
6
7
|
export declare function validateContextFileScopes(value: unknown, rolePath: string): readonly ContextFileScope[] | undefined;
|
|
7
8
|
export declare function validateWorkflowExtensionSettings(value: unknown, settingsPath: string, errorCode?: "INVALID_SETTINGS" | "INVALID_METADATA"): WorkflowExtensionSettings | undefined;
|
|
8
9
|
export declare function loadSettings(path?: string): Readonly<WorkflowSettings>;
|
|
10
|
+
/**
|
|
11
|
+
* Exposure and annotations of a workflow or subagent tool. Under the global `codemodeTools` setting,
|
|
12
|
+
* `model-only` keeps the model's direct access and stops codemode scripts from calling the tool.
|
|
13
|
+
* Tools are registered once at load, so the setting is global and applies after a reload.
|
|
14
|
+
* Read-only tools carry `readOnlyHint` so permission extensions need not confirm them.
|
|
15
|
+
*/
|
|
16
|
+
export declare function workflowToolExposure(name: string, codemodeTools: WorkflowSettings["codemodeTools"]): {
|
|
17
|
+
exposure?: "model-only";
|
|
18
|
+
annotations?: ToolAnnotations;
|
|
19
|
+
};
|
|
20
|
+
/** The `codemodeTools` setting at load time. Invalid settings keep the default; launches report them. */
|
|
21
|
+
export declare function loadCodemodeToolsSetting(agentDir?: string): WorkflowSettings["codemodeTools"];
|
|
9
22
|
export declare function loadSettingsOverrides(path: string): Readonly<WorkflowSettingsOverrides>;
|
|
10
23
|
export declare function resolveWorkflowSettings(cwd: string, projectTrusted: boolean, globalSettingsPath?: string): WorkflowSettingsResolution;
|
|
11
24
|
export declare function validateModelAliasAvailability(aliases: Readonly<Record<string, string>>, names: readonly string[], availableModels: ReadonlySet<string>, knownModels: ReadonlySet<string>, settingsPath?: string): void;
|
package/dist/src/settings.js
CHANGED
|
@@ -12,7 +12,8 @@ const ROLE_DIRECTORY = "pi-extensible-workflows";
|
|
|
12
12
|
export const DEFAULT_SETTINGS = Object.freeze({ concurrency: 8, backgroundWidget: true });
|
|
13
13
|
export function workflowSettingsPath(agentDir = getAgentDir()) { return join(agentDir, ROLE_DIRECTORY, "settings.json"); }
|
|
14
14
|
function normalizedResourcePath(value, settingsPath) {
|
|
15
|
-
|
|
15
|
+
// Built-in extensions are named `builtin:<name>`, not by a path.
|
|
16
|
+
if (value === "*" || value.startsWith("builtin:"))
|
|
16
17
|
return value;
|
|
17
18
|
let expanded = value === "~" ? homedir() : value.startsWith("~/") || value.startsWith("~\\") ? join(homedir(), value.slice(2)) : value;
|
|
18
19
|
if (expanded.startsWith("file://"))
|
|
@@ -152,7 +153,7 @@ function parseSettings(path, partial) {
|
|
|
152
153
|
}
|
|
153
154
|
if (!object(parsed))
|
|
154
155
|
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 allowed = new Set(["concurrency", "modelAliases", "skills", "extensions", "extensionSettings", "tools", "retention", "worktreePostCreateCommand", ...(partial ? [] : ["backgroundWidget", "codemodeTools"])]);
|
|
156
157
|
const unknown = Object.keys(parsed).find((key) => !allowed.has(key));
|
|
157
158
|
if (Object.prototype.hasOwnProperty.call(parsed, "disabledAgentResources"))
|
|
158
159
|
fail("INVALID_SETTINGS", `disabledAgentResources is no longer supported; use skills, extensions, and tools selectors (settings: ${path})`);
|
|
@@ -164,6 +165,9 @@ function parseSettings(path, partial) {
|
|
|
164
165
|
const backgroundWidget = parsed.backgroundWidget === undefined ? (partial ? undefined : DEFAULT_SETTINGS.backgroundWidget) : parsed.backgroundWidget;
|
|
165
166
|
if (backgroundWidget !== undefined && typeof backgroundWidget !== "boolean")
|
|
166
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"`);
|
|
167
171
|
const modelAliases = parsed.modelAliases === undefined ? undefined : validateModelAliases(parsed.modelAliases, path);
|
|
168
172
|
const skills = validateSelectorList(parsed.skills, path, "skills");
|
|
169
173
|
const tools = validateSelectorList(parsed.tools, path, "tools");
|
|
@@ -172,13 +176,33 @@ function parseSettings(path, partial) {
|
|
|
172
176
|
const retention = validateRetention(parsed.retention, path);
|
|
173
177
|
const worktreePostCreateCommand = validateWorktreePostCreateCommand(parsed.worktreePostCreateCommand, path);
|
|
174
178
|
return Object.freeze({
|
|
175
|
-
...(concurrency === undefined ? {} : { concurrency }), ...(backgroundWidget === undefined ? {} : { backgroundWidget }), ...(modelAliases === undefined ? {} : { modelAliases }),
|
|
179
|
+
...(concurrency === undefined ? {} : { concurrency }), ...(backgroundWidget === undefined ? {} : { backgroundWidget }), ...(codemodeTools === undefined ? {} : { codemodeTools }), ...(modelAliases === undefined ? {} : { modelAliases }),
|
|
176
180
|
...(skills === undefined ? {} : { skills }), ...(extensions === undefined ? {} : { extensions }),
|
|
177
181
|
...(extensionSettings === undefined ? {} : { extensionSettings }), ...(tools === undefined ? {} : { tools }), ...(retention === undefined ? {} : { retention }),
|
|
178
182
|
...(worktreePostCreateCommand === undefined ? {} : { worktreePostCreateCommand }),
|
|
179
183
|
});
|
|
180
184
|
}
|
|
181
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
|
+
}
|
|
182
206
|
export function loadSettingsOverrides(path) { return parseSettings(path, true); }
|
|
183
207
|
export function resolveWorkflowSettings(cwd, projectTrusted, globalSettingsPath = workflowSettingsPath()) {
|
|
184
208
|
const projectSettingsPath = workflowProjectSettingsPath(cwd);
|
package/dist/src/trajectory.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { type AwaitingCheckpoint, type CompletedOperation, type EffectiveSystemPrompt, type PersistedRun } from "./persistence.js";
|
|
2
|
-
import type { AgentAttemptSummary, AgentRecord, JsonValue, LaunchSnapshot, WorkflowAgentSessionReference } from "./types.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;
|
|
@@ -24,8 +24,17 @@ export type TrajectoryAgentOutput = {
|
|
|
24
24
|
export type TrajectoryAgent = AgentRecord & {
|
|
25
25
|
readonly output: TrajectoryAgentOutput;
|
|
26
26
|
};
|
|
27
|
-
export type
|
|
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"> & {
|
|
28
36
|
readonly agents: readonly TrajectoryAgent[];
|
|
37
|
+
readonly scriptCalls?: readonly TrajectoryScriptCall[];
|
|
29
38
|
};
|
|
30
39
|
export type TrajectoryRun = {
|
|
31
40
|
run: TrajectoryRunRecord;
|
|
@@ -135,6 +144,7 @@ export type TrajectoryPublisherInput = {
|
|
|
135
144
|
loadTranscript?: TrajectoryTranscriptLoader;
|
|
136
145
|
handleAction: TrajectoryActionHandler;
|
|
137
146
|
};
|
|
147
|
+
export declare function isTimingTranscriptEntry(value: unknown): boolean;
|
|
138
148
|
export declare function applySystemPrompts(run: PersistedRun, prompts: readonly EffectiveSystemPrompt[]): PersistedRun;
|
|
139
149
|
export declare function applyToolDescriptions(run: PersistedRun, catalog: ReadonlyMap<string, string>): PersistedRun;
|
|
140
150
|
export declare function withPiToolDescriptions(run: PersistedRun): PersistedRun;
|
package/dist/src/trajectory.js
CHANGED
|
@@ -9,6 +9,7 @@ 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;
|
|
14
15
|
function subagentOutput(options) {
|
|
@@ -50,7 +51,7 @@ function transcriptToolCallId(value) {
|
|
|
50
51
|
return part.id;
|
|
51
52
|
return undefined;
|
|
52
53
|
}
|
|
53
|
-
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; }
|
|
54
55
|
function timingToolCallId(value) {
|
|
55
56
|
if (!object(value) || !isTimingTranscriptEntry(value) || !object(value.data))
|
|
56
57
|
return undefined;
|
|
@@ -194,7 +195,8 @@ export function withPiToolDescriptionsForTools(tools, cwd) {
|
|
|
194
195
|
function canonicalExtensionSelector(selector, base) {
|
|
195
196
|
const negated = selector.startsWith("!");
|
|
196
197
|
const body = negated ? selector.slice(1) : selector;
|
|
197
|
-
|
|
198
|
+
// Built-in extensions are named `builtin:<name>`, not by a path.
|
|
199
|
+
if (body === "*" || body === "**" || body.startsWith("**/") || body.startsWith("builtin:"))
|
|
198
200
|
return selector;
|
|
199
201
|
const resolved = resolve(base, body);
|
|
200
202
|
if (resourcePatternHasMagic(body))
|
|
@@ -280,11 +282,20 @@ function failedAgentOutput(agent) {
|
|
|
280
282
|
const status = agent.state === "cancelled" ? "cancelled" : "failed";
|
|
281
283
|
return { status, code: error?.code ?? (status === "cancelled" ? "CANCELLED" : "AGENT_FAILED"), message: error?.message ?? `Agent ${agent.state}` };
|
|
282
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
|
+
}
|
|
283
296
|
function outputForAgent(agent, operation) {
|
|
284
|
-
if (operation !== undefined)
|
|
285
|
-
|
|
286
|
-
return bytes > DEFAULT_MAX_BYTES ? { status: "truncated", kind: "result", bytes } : { status: "available", value: operation.value, bytes };
|
|
287
|
-
}
|
|
297
|
+
if (operation !== undefined)
|
|
298
|
+
return operationOutput(operation);
|
|
288
299
|
if (agent.state === "failed" || agent.state === "cancelled")
|
|
289
300
|
return failedAgentOutput(agent);
|
|
290
301
|
if (agent.state === "completed")
|
|
@@ -294,7 +305,18 @@ function outputForAgent(agent, operation) {
|
|
|
294
305
|
export function applyTrajectoryAgentOutputs(run, operations) {
|
|
295
306
|
const byPath = new Map(operations.map((operation) => [operation.path, operation]));
|
|
296
307
|
const agents = run.agents.map((agent) => ({ ...agent, output: outputForAgent(agent, agent.resultPath === undefined ? undefined : byPath.get(agent.resultPath)) }));
|
|
297
|
-
|
|
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 }) };
|
|
298
320
|
}
|
|
299
321
|
function overlayTrajectoryRun(run, overlay) {
|
|
300
322
|
const overlaid = overlay(run);
|
|
@@ -304,7 +326,7 @@ function overlayTrajectoryRun(run, overlay) {
|
|
|
304
326
|
const output = previous?.status === "available" || previous?.status === "truncated" ? previous : outputForAgent(agent, undefined);
|
|
305
327
|
return { ...agent, output };
|
|
306
328
|
});
|
|
307
|
-
return { ...overlaid, agents };
|
|
329
|
+
return { ...overlaid, agents, ...(run.scriptCalls === undefined ? {} : { scriptCalls: run.scriptCalls }) };
|
|
308
330
|
}
|
|
309
331
|
async function loadTrajectoryRun(store, includeTranscripts = true) {
|
|
310
332
|
const value = await store.load();
|
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];
|
|
@@ -215,9 +215,11 @@ export interface WorkflowRetentionSettings {
|
|
|
215
215
|
olderThanDays?: number;
|
|
216
216
|
maxTerminalRuns?: number;
|
|
217
217
|
}
|
|
218
|
+
export type CodemodeToolsSetting = "all" | "read-only" | "none";
|
|
218
219
|
export interface WorkflowSettings {
|
|
219
220
|
concurrency: number;
|
|
220
221
|
backgroundWidget?: boolean;
|
|
222
|
+
codemodeTools?: CodemodeToolsSetting;
|
|
221
223
|
modelAliases?: Readonly<Record<string, string>>;
|
|
222
224
|
skills?: readonly string[];
|
|
223
225
|
extensions?: readonly string[];
|
|
@@ -226,7 +228,7 @@ export interface WorkflowSettings {
|
|
|
226
228
|
retention?: Readonly<WorkflowRetentionSettings>;
|
|
227
229
|
worktreePostCreateCommand?: readonly string[];
|
|
228
230
|
}
|
|
229
|
-
export type WorkflowSettingsOverrides = Partial<Omit<WorkflowSettings, "backgroundWidget">>;
|
|
231
|
+
export type WorkflowSettingsOverrides = Partial<Omit<WorkflowSettings, "backgroundWidget" | "codemodeTools">>;
|
|
230
232
|
export interface WorkflowSettingsSources {
|
|
231
233
|
concurrency: string;
|
|
232
234
|
modelAliases: string;
|
|
@@ -403,6 +405,21 @@ export interface WorkflowPhaseShellActivity {
|
|
|
403
405
|
active: number;
|
|
404
406
|
startedAt: number;
|
|
405
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
|
+
}
|
|
406
423
|
export interface RunRecord {
|
|
407
424
|
id: string;
|
|
408
425
|
workflowName: string;
|
|
@@ -419,6 +436,11 @@ export interface RunRecord {
|
|
|
419
436
|
activeShells?: number;
|
|
420
437
|
activeShellStartedAt?: number;
|
|
421
438
|
activeShellsByPhase?: readonly WorkflowPhaseShellActivity[];
|
|
439
|
+
activeTools?: readonly {
|
|
440
|
+
name: string;
|
|
441
|
+
startedAt: number;
|
|
442
|
+
}[];
|
|
443
|
+
scriptCalls?: readonly WorkflowScriptCall[];
|
|
422
444
|
error?: WorkflowErrorShape;
|
|
423
445
|
failedAt?: string;
|
|
424
446
|
budget?: WorkflowBudget;
|
|
@@ -835,6 +857,11 @@ export interface ShellIdentity {
|
|
|
835
857
|
occurrence: number;
|
|
836
858
|
worktreeOwner?: string;
|
|
837
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
|
+
}
|
|
838
865
|
export interface WorkflowBridge {
|
|
839
866
|
agent?: (prompt: string, options: Readonly<Record<string, JsonValue>>, signal: AbortSignal, identity: AgentIdentity) => Promise<JsonValue>;
|
|
840
867
|
shell?: (command: string, options: ShellOptions, signal: AbortSignal, identity: ShellIdentity) => Promise<ShellResult>;
|
|
@@ -846,6 +873,7 @@ export interface WorkflowBridge {
|
|
|
846
873
|
}>>;
|
|
847
874
|
phase?: (name: string) => void | Promise<void>;
|
|
848
875
|
log?: (message: string) => void | Promise<void>;
|
|
876
|
+
tool?: (identifier: string, args: Readonly<Record<string, JsonValue>>, signal: AbortSignal, identity: ToolIdentity) => Promise<JsonValue>;
|
|
849
877
|
}
|
|
850
878
|
export interface WorkflowExecution {
|
|
851
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
|
@@ -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;
|
package/dist/src/utils.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
1
2
|
import { ERROR_CODES, LAUNCH_SNAPSHOT_IDENTITY_VERSION, THINKING_LEVELS, WorkflowError } from "./types.js";
|
|
2
3
|
import { Minimatch } from "minimatch";
|
|
3
4
|
export class SerialLane {
|
|
@@ -17,6 +18,17 @@ export function sanitizeDisplayText(value) { return value.replace(ANSI_ESCAPE_SE
|
|
|
17
18
|
export { object as isObject };
|
|
18
19
|
function isStringKey(key) { return typeof key === "string"; }
|
|
19
20
|
function stringKeyValue(value, key) { return object(value) ? value[key] : undefined; }
|
|
21
|
+
function sortedJson(value) {
|
|
22
|
+
if (Array.isArray(value))
|
|
23
|
+
return value.map(sortedJson);
|
|
24
|
+
if (value === null || typeof value !== "object")
|
|
25
|
+
return value;
|
|
26
|
+
return Object.fromEntries(Object.keys(value).sort().map((key) => [key, sortedJson(value[key])]));
|
|
27
|
+
}
|
|
28
|
+
/** A short digest of a JSON value that ignores object key order. */
|
|
29
|
+
export function jsonDigest(value) {
|
|
30
|
+
return createHash("sha256").update(JSON.stringify(sortedJson(value))).digest("hex").slice(0, 16);
|
|
31
|
+
}
|
|
20
32
|
export function jsonValue(value, seen = new Set()) {
|
|
21
33
|
if (value === null || typeof value === "boolean" || typeof value === "string")
|
|
22
34
|
return true;
|
|
@@ -178,6 +190,24 @@ export function resolveModelReference(value, aliases = {}, knownModels, settings
|
|
|
178
190
|
};
|
|
179
191
|
return resolveReference(value, []);
|
|
180
192
|
}
|
|
193
|
+
/**
|
|
194
|
+
* Tools a session can reach, and so the ceiling for its agents: the declared tools plus the ones
|
|
195
|
+
* only codemode scripts call or tool_search loads, such as MCP tools with the default exposure.
|
|
196
|
+
* Agent sessions allow tools by name, so leaving these out would make them unreachable there.
|
|
197
|
+
*/
|
|
198
|
+
export function reachableTools(pi) {
|
|
199
|
+
const undeclared = pi.getAllTools?.().filter(({ exposure }) => exposure === "codemode" || exposure === "deferred").map(({ name }) => name) ?? [];
|
|
200
|
+
return [...new Set([...pi.getActiveTools(), ...undeclared])];
|
|
201
|
+
}
|
|
202
|
+
/** Provider of the virtual models that expose workflow model aliases in `/model`. */
|
|
203
|
+
export const VIRTUAL_MODEL_PROVIDER = "workflow";
|
|
204
|
+
/** Child sessions do not load the workflow extension, so they receive the alias target instead of its virtual model. */
|
|
205
|
+
export function physicalModel(spec, aliases = {}, knownModels, settingsPath) {
|
|
206
|
+
if (spec.provider !== VIRTUAL_MODEL_PROVIDER || !modelAliasName(spec.model, aliases))
|
|
207
|
+
return spec;
|
|
208
|
+
const target = resolveModelReference(spec.model, aliases, knownModels, settingsPath);
|
|
209
|
+
return spec.thinking === undefined ? target : { ...target, thinking: spec.thinking };
|
|
210
|
+
}
|
|
181
211
|
export function modelCapability(value, aliases, knownModels, settingsPath) {
|
|
182
212
|
const parsed = typeof value === "string" ? resolveModelReference(value, aliases, knownModels, settingsPath) : value;
|
|
183
213
|
return `${parsed.provider}/${parsed.model}`;
|
package/dist/src/validation.d.ts
CHANGED
|
@@ -3,8 +3,13 @@ import type { WorkflowRegistryApi } from "./registry.js";
|
|
|
3
3
|
export { validateSchema } from "./utils.js";
|
|
4
4
|
export { loadAgentDefinitions, loadProjectAgentDefinitions, parseRoleMarkdown, workflowRoleDirectories } from "./roles.js";
|
|
5
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";
|
|
6
|
+
export { DEFAULT_SETTINGS, loadCodemodeToolsSetting, loadSettings, loadSettingsOverrides, resolveAgentResourcePolicy, resolveWorkflowSettings, saveModelAliases, validateContextFileScopes, validateModelAliasAvailability, validateSelectorList, validateWorkflowExtensionSettings, workflowProjectSettingsPath, workflowSettingsPath, workflowToolExposure } from "./settings.js";
|
|
7
7
|
export declare function validateCheckpoint(value: unknown): CheckpointInput;
|
|
8
|
+
/**
|
|
9
|
+
* The `tools.<id>` members a workflow script names statically. Any other use of `tools`, such as a
|
|
10
|
+
* local declaration that shadows the global or passing it around, makes the set unknowable: undefined.
|
|
11
|
+
*/
|
|
12
|
+
export declare function scriptToolReferences(script: string): string[] | undefined;
|
|
8
13
|
export declare function instrumentWorkflow(script: string): string;
|
|
9
14
|
export declare function workflowPrompt(template: string, values: Readonly<Record<string, JsonValue>>): string;
|
|
10
15
|
export declare function validateAgentOptions(value: unknown): Readonly<Record<string, JsonValue>>;
|
package/dist/src/validation.js
CHANGED
|
@@ -8,7 +8,7 @@ export { validateSchema } from "./utils.js";
|
|
|
8
8
|
import { loadAgentDefinitions, loadProjectAgentDefinitions } from "./roles.js";
|
|
9
9
|
export { loadAgentDefinitions, loadProjectAgentDefinitions, parseRoleMarkdown, workflowRoleDirectories } from "./roles.js";
|
|
10
10
|
import { validateContextFileScopes, validateSelectorList } from "./settings.js";
|
|
11
|
-
export { DEFAULT_SETTINGS, loadSettings, loadSettingsOverrides, resolveAgentResourcePolicy, resolveWorkflowSettings, saveModelAliases, validateContextFileScopes, validateModelAliasAvailability, validateSelectorList, validateWorkflowExtensionSettings, workflowProjectSettingsPath, workflowSettingsPath } from "./settings.js";
|
|
11
|
+
export { DEFAULT_SETTINGS, loadCodemodeToolsSetting, loadSettings, loadSettingsOverrides, resolveAgentResourcePolicy, resolveWorkflowSettings, saveModelAliases, validateContextFileScopes, validateModelAliasAvailability, validateSelectorList, validateWorkflowExtensionSettings, workflowProjectSettingsPath, workflowSettingsPath, workflowToolExposure } from "./settings.js";
|
|
12
12
|
export function validateCheckpoint(value) {
|
|
13
13
|
if (!object(value) || Object.keys(value).some((key) => !["name", "prompt", "context"].includes(key)) || typeof value.name !== "string" || value.name.trim() === "" || typeof value.prompt !== "string" || !jsonValue(value.context))
|
|
14
14
|
fail("INVALID_METADATA", "checkpoint requires only name, prompt, and JSON context");
|
|
@@ -244,6 +244,26 @@ function callHasTrailingComma(source, call) {
|
|
|
244
244
|
}
|
|
245
245
|
return current?.type.label === ")" && previous?.type.label === ",";
|
|
246
246
|
}
|
|
247
|
+
/**
|
|
248
|
+
* The `tools.<id>` members a workflow script names statically. Any other use of `tools`, such as a
|
|
249
|
+
* local declaration that shadows the global or passing it around, makes the set unknowable: undefined.
|
|
250
|
+
*/
|
|
251
|
+
export function scriptToolReferences(script) {
|
|
252
|
+
const body = workflowBody(script);
|
|
253
|
+
if (!body.trim())
|
|
254
|
+
return [];
|
|
255
|
+
const names = new Set();
|
|
256
|
+
const opaque = (node, parent) => {
|
|
257
|
+
if (node.type === "Identifier" && node.name === "tools") {
|
|
258
|
+
if (parent?.type === "MemberExpression" && parent.object === node && !parent.computed && parent.property.type === "Identifier")
|
|
259
|
+
names.add(parent.property.name);
|
|
260
|
+
else if (!(parent?.type === "Property" && parent.key === node && !parent.computed && !parent.shorthand || parent?.type === "MemberExpression" && parent.property === node && !parent.computed))
|
|
261
|
+
return true;
|
|
262
|
+
}
|
|
263
|
+
return astChildren(node).some((child) => opaque(child, node));
|
|
264
|
+
};
|
|
265
|
+
return opaque(parseWorkflow(body)) ? undefined : [...names];
|
|
266
|
+
}
|
|
247
267
|
export function instrumentWorkflow(script) {
|
|
248
268
|
const body = workflowBody(script);
|
|
249
269
|
if (!body.trim())
|
package/dist/starter/index.js
CHANGED