pi-extensible-workflows 3.0.0 → 3.2.0
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/README.md +15 -17
- package/dist/src/agent-execution.d.ts +4 -80
- package/dist/src/agent-execution.js +20 -10
- package/dist/src/cli.d.ts +2 -0
- package/dist/src/cli.js +48 -2
- package/dist/src/doctor-cleanup.d.ts +41 -0
- package/dist/src/doctor-cleanup.js +597 -0
- package/dist/src/doctor.d.ts +7 -2
- package/dist/src/doctor.js +127 -22
- package/dist/src/execution.js +13 -4
- package/dist/src/host.d.ts +83 -4
- package/dist/src/host.js +1246 -410
- package/dist/src/index.d.ts +5 -2
- package/dist/src/index.js +3 -1
- package/dist/src/persistence.d.ts +3 -0
- package/dist/src/persistence.js +49 -1
- package/dist/src/registry.d.ts +21 -9
- package/dist/src/registry.js +131 -21
- package/dist/src/session-inspector.js +4 -2
- package/dist/src/types.d.ts +135 -7
- package/dist/src/utils.d.ts +6 -0
- package/dist/src/utils.js +45 -5
- package/dist/src/validation.d.ts +6 -2
- package/dist/src/validation.js +157 -31
- package/dist/src/workflow-artifacts.d.ts +13 -0
- package/dist/src/workflow-artifacts.js +39 -0
- package/examples/workflow-extension-template/README.md +37 -0
- package/examples/workflow-extension-template/extension.test.mjs +59 -0
- package/examples/workflow-extension-template/index.js +51 -0
- package/examples/workflow-extension-template/roles/reviewer.md +4 -0
- package/package.json +5 -3
- package/skills/pi-extensible-workflows/SKILL.md +45 -18
- package/src/agent-execution.ts +23 -37
- package/src/cli.ts +33 -2
- package/src/doctor-cleanup.ts +337 -0
- package/src/doctor.ts +117 -24
- package/src/execution.ts +13 -4
- package/src/host.ts +1039 -366
- package/src/index.ts +5 -2
- package/src/persistence.ts +35 -1
- package/src/registry.ts +108 -25
- package/src/session-inspector.ts +4 -2
- package/src/types.ts +53 -8
- package/src/utils.ts +39 -5
- package/src/validation.ts +130 -31
- package/src/workflow-artifacts.ts +34 -0
package/dist/src/types.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { AgentSessionEvent, CreateAgentSessionOptions, InlineExtension, SessionStats, ToolDefinition } from "@earendil-works/pi-coding-agent";
|
|
1
2
|
export declare const RUN_STATES: readonly ["queued", "running", "pausing", "paused", "awaiting_input", "completed", "failed", "stopped", "interrupted", "budget_exhausted"];
|
|
2
3
|
export declare const AGENT_STATES: readonly ["queued", "running", "waiting_for_child", "paused", "retrying", "completed", "failed", "cancelled"];
|
|
3
4
|
export declare const WORKFLOW_CALL_KINDS: readonly ["agent", "parallel", "pipeline", "checkpoint", "phase", "withWorktree", "shell"];
|
|
@@ -141,6 +142,17 @@ export interface ModelSpec {
|
|
|
141
142
|
model: string;
|
|
142
143
|
thinking?: "off" | "minimal" | "low" | "medium" | "high" | "xhigh" | "max";
|
|
143
144
|
}
|
|
145
|
+
export interface WorkflowModelAliasResolverContext {
|
|
146
|
+
cwd: string;
|
|
147
|
+
projectTrusted: boolean;
|
|
148
|
+
rootModel: ModelSpec;
|
|
149
|
+
knownModels: ReadonlySet<string>;
|
|
150
|
+
availableModels: ReadonlySet<string>;
|
|
151
|
+
signal: AbortSignal;
|
|
152
|
+
}
|
|
153
|
+
export interface WorkflowModelAlias {
|
|
154
|
+
resolve: (context: Readonly<WorkflowModelAliasResolverContext>) => string | Promise<string>;
|
|
155
|
+
}
|
|
144
156
|
export interface WorkflowMetadata {
|
|
145
157
|
name: string;
|
|
146
158
|
description?: string;
|
|
@@ -150,6 +162,25 @@ export interface WorkflowSettings {
|
|
|
150
162
|
modelAliases?: Readonly<Record<string, string>>;
|
|
151
163
|
disabledAgentResources?: Readonly<AgentResourceExclusions>;
|
|
152
164
|
}
|
|
165
|
+
export interface WorkflowSettingsOverrides {
|
|
166
|
+
concurrency?: number;
|
|
167
|
+
modelAliases?: Readonly<Record<string, string>>;
|
|
168
|
+
disabledAgentResources?: Readonly<AgentResourceExclusions>;
|
|
169
|
+
}
|
|
170
|
+
export interface WorkflowSettingsSources {
|
|
171
|
+
concurrency: string;
|
|
172
|
+
modelAliases: string;
|
|
173
|
+
disabledAgentResources: string;
|
|
174
|
+
}
|
|
175
|
+
export interface WorkflowSettingsResolution {
|
|
176
|
+
globalSettingsPath: string;
|
|
177
|
+
projectSettingsPath: string;
|
|
178
|
+
projectTrusted: boolean;
|
|
179
|
+
global: Readonly<WorkflowSettings>;
|
|
180
|
+
project: Readonly<WorkflowSettingsOverrides>;
|
|
181
|
+
effective: Readonly<WorkflowSettings>;
|
|
182
|
+
sources: Readonly<WorkflowSettingsSources>;
|
|
183
|
+
}
|
|
153
184
|
export interface AgentResourceExclusions {
|
|
154
185
|
skills: readonly string[];
|
|
155
186
|
extensions: readonly string[];
|
|
@@ -163,6 +194,8 @@ export interface AgentResourcePolicy {
|
|
|
163
194
|
effective: AgentResourceExclusions;
|
|
164
195
|
unmatchedSkills: string[];
|
|
165
196
|
unmatchedExtensions: string[];
|
|
197
|
+
excludedSkills?: string[];
|
|
198
|
+
excludedExtensions?: string[];
|
|
166
199
|
}
|
|
167
200
|
export interface AgentActivity {
|
|
168
201
|
kind: "reasoning" | "tool" | "text";
|
|
@@ -183,6 +216,8 @@ export interface AgentSetupSummary {
|
|
|
183
216
|
disabledAgentResources?: {
|
|
184
217
|
skills: readonly string[];
|
|
185
218
|
extensions: readonly string[];
|
|
219
|
+
excludedSkills?: readonly string[];
|
|
220
|
+
excludedExtensions?: readonly string[];
|
|
186
221
|
unmatchedSkills: readonly string[];
|
|
187
222
|
unmatchedExtensions: readonly string[];
|
|
188
223
|
};
|
|
@@ -222,6 +257,7 @@ export interface AgentRecord {
|
|
|
222
257
|
state: AgentState;
|
|
223
258
|
parentId?: string;
|
|
224
259
|
structuralPath?: readonly string[];
|
|
260
|
+
resultPath?: string;
|
|
225
261
|
parentBreadcrumb?: string;
|
|
226
262
|
worktreeOwner?: string;
|
|
227
263
|
role?: string;
|
|
@@ -294,9 +330,11 @@ export interface LaunchSnapshot {
|
|
|
294
330
|
args: JsonValue;
|
|
295
331
|
metadata: WorkflowMetadata;
|
|
296
332
|
settings: WorkflowSettings;
|
|
333
|
+
settingsSources?: WorkflowSettingsSources;
|
|
297
334
|
budget?: WorkflowBudget;
|
|
298
335
|
settingsPath?: string;
|
|
299
336
|
modelAliases?: Readonly<Record<string, string>>;
|
|
337
|
+
phases?: readonly string[];
|
|
300
338
|
models: readonly string[];
|
|
301
339
|
tools: readonly string[];
|
|
302
340
|
agentTypes: readonly string[];
|
|
@@ -359,14 +397,69 @@ export interface WorkflowVariable {
|
|
|
359
397
|
schema: JsonSchema;
|
|
360
398
|
resolve: (run: Readonly<WorkflowRunContext>) => Promise<JsonValue> | JsonValue;
|
|
361
399
|
}
|
|
400
|
+
type NativeAgentMessage = {
|
|
401
|
+
role: string;
|
|
402
|
+
content?: unknown;
|
|
403
|
+
stopReason?: string;
|
|
404
|
+
errorMessage?: string;
|
|
405
|
+
usage?: {
|
|
406
|
+
input: number;
|
|
407
|
+
output: number;
|
|
408
|
+
cacheRead: number;
|
|
409
|
+
cacheWrite: number;
|
|
410
|
+
cost: {
|
|
411
|
+
total: number;
|
|
412
|
+
};
|
|
413
|
+
};
|
|
414
|
+
};
|
|
415
|
+
type NativeSessionStats = Pick<SessionStats, "tokens" | "cost">;
|
|
416
|
+
export interface NativeSession {
|
|
417
|
+
readonly sessionId: string;
|
|
418
|
+
readonly sessionFile: string | undefined;
|
|
419
|
+
readonly messages: readonly NativeAgentMessage[];
|
|
420
|
+
getSessionStats(): NativeSessionStats;
|
|
421
|
+
readonly systemPrompt?: string;
|
|
422
|
+
readonly model?: {
|
|
423
|
+
provider: string;
|
|
424
|
+
model?: string;
|
|
425
|
+
id?: string;
|
|
426
|
+
};
|
|
427
|
+
readonly agent?: {
|
|
428
|
+
state: {
|
|
429
|
+
tools: readonly {
|
|
430
|
+
name: string;
|
|
431
|
+
}[];
|
|
432
|
+
};
|
|
433
|
+
};
|
|
434
|
+
getLeafId?: () => string | null;
|
|
435
|
+
getToolDefinitions?: () => unknown;
|
|
436
|
+
subscribe?(listener: (event: AgentSessionEvent) => void): () => void;
|
|
437
|
+
prompt(text: string): Promise<void>;
|
|
438
|
+
steer?(text: string): Promise<void>;
|
|
439
|
+
abort?(): Promise<void>;
|
|
440
|
+
dispose(): void;
|
|
441
|
+
}
|
|
442
|
+
type SessionTools = NonNullable<CreateAgentSessionOptions["tools"]>;
|
|
443
|
+
type SessionCustomTools = NonNullable<CreateAgentSessionOptions["customTools"]>;
|
|
444
|
+
export interface SessionInput {
|
|
445
|
+
cwd: string;
|
|
446
|
+
model: ModelSpec;
|
|
447
|
+
tools: SessionTools;
|
|
448
|
+
sessionLabel: string;
|
|
449
|
+
agentDir?: string;
|
|
450
|
+
customTools?: SessionCustomTools;
|
|
451
|
+
resultTool?: ToolDefinition;
|
|
452
|
+
systemPromptAppend?: string;
|
|
453
|
+
extensionFactories?: InlineExtension[];
|
|
454
|
+
resourcePolicy?: AgentResourcePolicy;
|
|
455
|
+
options?: AgentOptions;
|
|
456
|
+
}
|
|
457
|
+
export type SessionFactory = (input: SessionInput) => Promise<NativeSession>;
|
|
362
458
|
export interface AgentSetup {
|
|
363
459
|
prompt: string;
|
|
364
|
-
options:
|
|
365
|
-
sessionInput:
|
|
366
|
-
|
|
367
|
-
[key: string]: unknown;
|
|
368
|
-
};
|
|
369
|
-
createSession: unknown;
|
|
460
|
+
options: AgentOptions;
|
|
461
|
+
sessionInput: SessionInput;
|
|
462
|
+
createSession: SessionFactory;
|
|
370
463
|
}
|
|
371
464
|
export interface AgentSetupContext {
|
|
372
465
|
readonly run: Readonly<WorkflowRunContext>;
|
|
@@ -383,13 +476,21 @@ export interface RegisteredAgentSetupHook {
|
|
|
383
476
|
priority: number;
|
|
384
477
|
setup: AgentSetupHook["setup"];
|
|
385
478
|
}
|
|
386
|
-
export interface
|
|
479
|
+
export interface WorkflowExtensionMetadata {
|
|
387
480
|
version: string;
|
|
388
481
|
headline: string;
|
|
389
482
|
description: string;
|
|
483
|
+
}
|
|
484
|
+
export interface WorkflowRoleDirectoryRegistration {
|
|
485
|
+
path: string;
|
|
486
|
+
extension: WorkflowExtensionMetadata;
|
|
487
|
+
}
|
|
488
|
+
export interface WorkflowExtension extends WorkflowExtensionMetadata {
|
|
390
489
|
functions?: Readonly<Record<string, WorkflowFunction>>;
|
|
391
490
|
variables?: Readonly<Record<string, WorkflowVariable>>;
|
|
491
|
+
modelAliases?: Readonly<Record<string, WorkflowModelAlias>>;
|
|
392
492
|
agentSetupHooks?: Readonly<Record<string, AgentSetupHook>>;
|
|
493
|
+
roleDirectories?: readonly (string | URL)[];
|
|
393
494
|
}
|
|
394
495
|
export interface WorkflowJournal {
|
|
395
496
|
get(path: string): JsonValue | undefined;
|
|
@@ -511,10 +612,34 @@ export interface WorkflowCatalogVariable {
|
|
|
511
612
|
description: string;
|
|
512
613
|
schema: JsonSchema;
|
|
513
614
|
}
|
|
615
|
+
export interface WorkflowCatalogModelAlias {
|
|
616
|
+
name: string;
|
|
617
|
+
kind: "static" | "dynamic";
|
|
618
|
+
provenance: string;
|
|
619
|
+
version?: string;
|
|
620
|
+
headline?: string;
|
|
621
|
+
extensionDescription?: string;
|
|
622
|
+
}
|
|
623
|
+
export interface WorkflowCatalogSettings {
|
|
624
|
+
concurrency: number;
|
|
625
|
+
modelAliases: Readonly<Record<string, string>>;
|
|
626
|
+
disabledAgentResources: AgentResourceExclusions;
|
|
627
|
+
globalSettingsPath: string;
|
|
628
|
+
projectSettingsPath: string;
|
|
629
|
+
projectTrusted: boolean;
|
|
630
|
+
sources: WorkflowSettingsSources;
|
|
631
|
+
}
|
|
632
|
+
export interface WorkflowCatalogContext {
|
|
633
|
+
cwd: string;
|
|
634
|
+
projectTrusted: boolean;
|
|
635
|
+
globalSettingsPath?: string;
|
|
636
|
+
}
|
|
514
637
|
export interface WorkflowCatalog {
|
|
515
638
|
functions: readonly WorkflowCatalogFunction[];
|
|
516
639
|
variables: readonly WorkflowCatalogVariable[];
|
|
517
640
|
modelAliases?: Readonly<Record<string, string>>;
|
|
641
|
+
modelAliasEntries?: readonly WorkflowCatalogModelAlias[];
|
|
642
|
+
settings?: WorkflowCatalogSettings;
|
|
518
643
|
}
|
|
519
644
|
export interface WorkflowCatalogIndexFunction {
|
|
520
645
|
name: string;
|
|
@@ -530,6 +655,8 @@ export interface WorkflowCatalogIndex {
|
|
|
530
655
|
functions: readonly WorkflowCatalogIndexFunction[];
|
|
531
656
|
variables: readonly WorkflowCatalogIndexVariable[];
|
|
532
657
|
modelAliases?: Readonly<Record<string, string>>;
|
|
658
|
+
modelAliasEntries?: readonly WorkflowCatalogModelAlias[];
|
|
659
|
+
settings?: WorkflowCatalogSettings;
|
|
533
660
|
}
|
|
534
661
|
export interface WorkflowCatalogError {
|
|
535
662
|
error: {
|
|
@@ -563,3 +690,4 @@ export interface ValidatedWorkflowLaunch {
|
|
|
563
690
|
roleNames: readonly string[];
|
|
564
691
|
functionName?: string;
|
|
565
692
|
}
|
|
693
|
+
export {};
|
package/dist/src/utils.d.ts
CHANGED
|
@@ -13,12 +13,18 @@ export declare function asWorkflowError(error: unknown): WorkflowError;
|
|
|
13
13
|
export declare function fail(code: WorkflowErrorCode, message: string): never;
|
|
14
14
|
export declare function parseThinking(value: unknown): ModelSpec["thinking"] | undefined;
|
|
15
15
|
export declare function parseModelReference(value: string): ModelSpec;
|
|
16
|
+
export declare function annotateModelAliasError(error: unknown, name: string): unknown;
|
|
17
|
+
export declare function modelAliasErrorName(error: unknown): string | undefined;
|
|
16
18
|
export declare function modelAliasName(value: string, aliases: Readonly<Record<string, string>>): string | undefined;
|
|
17
19
|
export declare function validateModelAliases(value: unknown, settingsPath?: string): Readonly<Record<string, string>>;
|
|
18
20
|
export declare function unknownModel(value: string, target: string | undefined, settingsPath?: string): never;
|
|
19
21
|
export declare function resolveModelReference(value: string, aliases?: Readonly<Record<string, string>>, knownModels?: ReadonlySet<string>, settingsPath?: string): ModelSpec;
|
|
20
22
|
export declare function modelCapability(value: string | ModelSpec, aliases?: Readonly<Record<string, string>>, knownModels?: ReadonlySet<string>, settingsPath?: string): string;
|
|
21
23
|
export declare function aliasDrift(previous: Readonly<Record<string, string>>, current: Readonly<Record<string, string>>): string[];
|
|
24
|
+
export declare function validateResourcePattern(pattern: string): void;
|
|
25
|
+
export declare function resourcePatternMatches(resource: string, pattern: string): boolean;
|
|
26
|
+
export declare function disabledResources(patterns: readonly string[], resources: readonly string[]): string[];
|
|
27
|
+
export declare function unmatchedResourcePatterns(patterns: readonly string[], resources: readonly string[]): string[];
|
|
22
28
|
export declare function mergeAgentResourceExclusions(...values: (AgentResourceExclusions | undefined)[]): AgentResourceExclusions;
|
|
23
29
|
export declare function createLaunchSnapshot(input: Omit<import("./types.js").LaunchSnapshot, "identityVersion"> & {
|
|
24
30
|
identityVersion?: number;
|
package/dist/src/utils.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { ERROR_CODES, LAUNCH_SNAPSHOT_IDENTITY_VERSION, WorkflowError } from "./types.js";
|
|
2
|
+
import { Minimatch } from "minimatch";
|
|
2
3
|
export function object(value) { return typeof value === "object" && value !== null && !Array.isArray(value); }
|
|
3
4
|
export { object as isObject };
|
|
4
5
|
export function jsonValue(value, seen = new Set()) {
|
|
@@ -61,7 +62,21 @@ export function parseModelReference(value) {
|
|
|
61
62
|
fail("UNKNOWN_MODEL", `Invalid thinking level: ${thinking}`);
|
|
62
63
|
return { provider: match[1], model: match[2], ...(thinking ? { thinking: thinking } : {}) };
|
|
63
64
|
}
|
|
64
|
-
|
|
65
|
+
const MODEL_ALIAS_ERROR_NAME = Symbol("modelAliasErrorName");
|
|
66
|
+
function aliasError(message, settingsPath, name) {
|
|
67
|
+
const error = new WorkflowError("CONFIG_ERROR", `${message} (settings: ${settingsPath})`);
|
|
68
|
+
if (name)
|
|
69
|
+
Object.defineProperty(error, MODEL_ALIAS_ERROR_NAME, { value: name, configurable: true });
|
|
70
|
+
throw error;
|
|
71
|
+
}
|
|
72
|
+
export function annotateModelAliasError(error, name) {
|
|
73
|
+
if (error instanceof WorkflowError)
|
|
74
|
+
Object.defineProperty(error, MODEL_ALIAS_ERROR_NAME, { value: name, configurable: true });
|
|
75
|
+
return error;
|
|
76
|
+
}
|
|
77
|
+
export function modelAliasErrorName(error) {
|
|
78
|
+
return error instanceof WorkflowError ? error[MODEL_ALIAS_ERROR_NAME] : undefined;
|
|
79
|
+
}
|
|
65
80
|
export function modelAliasName(value, aliases) {
|
|
66
81
|
const name = /^([^/:\s]+)(?::[^:\s]+)?$/.exec(value)?.[1];
|
|
67
82
|
return name && Object.prototype.hasOwnProperty.call(aliases, name) ? name : undefined;
|
|
@@ -72,9 +87,9 @@ export function validateModelAliases(value, settingsPath = "workflow settings")
|
|
|
72
87
|
const aliases = {};
|
|
73
88
|
for (const [name, target] of Object.entries(value)) {
|
|
74
89
|
if (!MODEL_ALIAS_NAME.test(name))
|
|
75
|
-
aliasError(`Invalid model alias name: ${name}`, settingsPath);
|
|
90
|
+
aliasError(`Invalid model alias name: ${name}`, settingsPath, name);
|
|
76
91
|
if (typeof target !== "string" || !target.trim())
|
|
77
|
-
aliasError(`Invalid model alias target for ${name}`, settingsPath);
|
|
92
|
+
aliasError(`Invalid model alias target for ${name}`, settingsPath, name);
|
|
78
93
|
aliases[name] = target;
|
|
79
94
|
}
|
|
80
95
|
for (const name of Object.keys(aliases)) {
|
|
@@ -82,7 +97,7 @@ export function validateModelAliases(value, settingsPath = "workflow settings")
|
|
|
82
97
|
resolveModelReference(name, aliases);
|
|
83
98
|
}
|
|
84
99
|
catch (error) {
|
|
85
|
-
aliasError(`Invalid model alias target for ${name}: ${errorText(error)}`, settingsPath);
|
|
100
|
+
aliasError(`Invalid model alias target for ${name}: ${errorText(error)}`, settingsPath, name);
|
|
86
101
|
}
|
|
87
102
|
}
|
|
88
103
|
return Object.freeze(aliases);
|
|
@@ -123,6 +138,31 @@ export function modelCapability(value, aliases, knownModels, settingsPath) {
|
|
|
123
138
|
export function aliasDrift(previous, current) {
|
|
124
139
|
return [...new Set([...Object.keys(previous), ...Object.keys(current)])].sort().flatMap((name) => previous[name] === current[name] ? [] : [`${name}: ${previous[name] ?? "(missing)"} -> ${current[name] ?? "(missing)"}`]);
|
|
125
140
|
}
|
|
126
|
-
|
|
141
|
+
const RESOURCE_PATTERN_OPTIONS = { dot: true, nonegate: true, nocomment: true };
|
|
142
|
+
function resourcePatternBody(pattern) { return pattern.startsWith("!") ? pattern.slice(1) : pattern; }
|
|
143
|
+
function resourcePatternPath(value) { return value.replaceAll("\\", "/"); }
|
|
144
|
+
export function validateResourcePattern(pattern) {
|
|
145
|
+
const body = resourcePatternBody(pattern);
|
|
146
|
+
if (!body)
|
|
147
|
+
throw new Error(`Empty minimatch pattern ${JSON.stringify(pattern)}`);
|
|
148
|
+
const matcher = new Minimatch(resourcePatternPath(body), RESOURCE_PATTERN_OPTIONS);
|
|
149
|
+
if (matcher.makeRe() === false)
|
|
150
|
+
throw new Error(`Invalid minimatch pattern ${JSON.stringify(pattern)}`);
|
|
151
|
+
}
|
|
152
|
+
export function resourcePatternMatches(resource, pattern) { return new Minimatch(resourcePatternPath(resourcePatternBody(pattern)), RESOURCE_PATTERN_OPTIONS).match(resourcePatternPath(resource)); }
|
|
153
|
+
export function disabledResources(patterns, resources) {
|
|
154
|
+
const disabled = new Set();
|
|
155
|
+
for (const resource of resources) {
|
|
156
|
+
let excluded = false;
|
|
157
|
+
for (const pattern of patterns)
|
|
158
|
+
if (resourcePatternMatches(resource, pattern))
|
|
159
|
+
excluded = !pattern.startsWith("!");
|
|
160
|
+
if (excluded)
|
|
161
|
+
disabled.add(resource);
|
|
162
|
+
}
|
|
163
|
+
return [...disabled];
|
|
164
|
+
}
|
|
165
|
+
export function unmatchedResourcePatterns(patterns, resources) { return patterns.filter((pattern) => !resources.some((resource) => resourcePatternMatches(resource, pattern))); }
|
|
166
|
+
export function mergeAgentResourceExclusions(...values) { return { skills: values.flatMap((value) => value?.skills ?? []), extensions: values.flatMap((value) => value?.extensions ?? []) }; }
|
|
127
167
|
export function createLaunchSnapshot(input) { return deepFreeze(structuredClone({ ...input, launchKind: input.launchKind ?? (input.functionName ? "function" : "inline"), identityVersion: input.identityVersion ?? LAUNCH_SNAPSHOT_IDENTITY_VERSION })); }
|
|
128
168
|
export function loadLaunchSnapshot(input) { return deepFreeze(structuredClone(input)); }
|
package/dist/src/validation.d.ts
CHANGED
|
@@ -1,15 +1,19 @@
|
|
|
1
|
-
import type { AgentDefinition, AgentResourcePolicy, CheckpointInput, JsonSchema, JsonValue, LaunchSnapshot, PreflightCapabilities, PreflightResult, ShellOptions, StaticWorkflowCall, ValidatedWorkflowLaunch, WorkflowMetadata, WorkflowSettings, WorkflowValidationContext, WorkflowValidationParameters } from "./types.js";
|
|
1
|
+
import type { AgentDefinition, AgentResourcePolicy, CheckpointInput, JsonSchema, JsonValue, LaunchSnapshot, PreflightCapabilities, PreflightResult, ShellOptions, StaticWorkflowCall, ValidatedWorkflowLaunch, WorkflowMetadata, WorkflowRoleDirectoryRegistration, WorkflowSettings, WorkflowSettingsOverrides, WorkflowSettingsResolution, WorkflowValidationContext, WorkflowValidationParameters } from "./types.js";
|
|
2
2
|
import type { WorkflowRegistryApi } from "./registry.js";
|
|
3
3
|
export declare const DEFAULT_SETTINGS: Readonly<WorkflowSettings>;
|
|
4
4
|
export declare function validateCheckpoint(value: unknown): CheckpointInput;
|
|
5
5
|
export declare function workflowSettingsPath(agentDir?: string): string;
|
|
6
6
|
export declare function workflowProjectSettingsPath(cwd: string): string;
|
|
7
7
|
export declare function loadSettings(path?: string): Readonly<WorkflowSettings>;
|
|
8
|
+
export declare function loadSettingsOverrides(path: string): Readonly<WorkflowSettingsOverrides>;
|
|
9
|
+
export declare function resolveWorkflowSettings(cwd: string, projectTrusted: boolean, globalSettingsPath?: string): WorkflowSettingsResolution;
|
|
10
|
+
export declare function validateModelAliasAvailability(aliases: Readonly<Record<string, string>>, names: readonly string[], availableModels: ReadonlySet<string>, knownModels: ReadonlySet<string>, settingsPath?: string): void;
|
|
8
11
|
export declare function resolveAgentResourcePolicy(cwd: string, projectTrusted: boolean, globalSettingsPath?: string): AgentResourcePolicy;
|
|
9
12
|
export declare function saveModelAliases(path?: string, aliases?: Readonly<Record<string, string>>): void;
|
|
10
13
|
export declare function parseRoleMarkdown(content: string, strict?: boolean, rolePath?: string): AgentDefinition;
|
|
11
14
|
export declare function workflowRoleDirectories(agentDir?: string): readonly string[];
|
|
12
|
-
export
|
|
15
|
+
export type WorkflowRoleDirectoryInput = string | WorkflowRoleDirectoryRegistration;
|
|
16
|
+
export declare function loadAgentDefinitions(cwd: string, agentDir?: string, projectTrusted?: boolean, extensionRoleDirectories?: readonly WorkflowRoleDirectoryInput[]): Readonly<Record<string, AgentDefinition>>;
|
|
13
17
|
export declare function instrumentWorkflow(script: string): string;
|
|
14
18
|
export declare function workflowPrompt(template: string, values: Readonly<Record<string, JsonValue>>): string;
|
|
15
19
|
export declare function validateSchema(schema: unknown, at?: string): asserts schema is JsonSchema;
|
package/dist/src/validation.js
CHANGED
|
@@ -8,7 +8,8 @@ import { Script } from "node:vm";
|
|
|
8
8
|
import { Value } from "typebox/value";
|
|
9
9
|
import { getAgentDir, parseFrontmatter } from "@earendil-works/pi-coding-agent";
|
|
10
10
|
import { WorkflowError } from "./types.js";
|
|
11
|
-
import {
|
|
11
|
+
import { registeredWorkflowRoleDirectoryRegistrations } from "./registry.js";
|
|
12
|
+
import { annotateModelAliasError, deepFreeze, errorText, fail, jsonValue, modelAliasName, modelCapability, object, parseThinking, positiveInteger, resolveModelReference, unknownModel, validateModelAliases, validateResourcePattern } from "./utils.js";
|
|
12
13
|
import { WORKFLOW_CALL_KINDS } from "./types.js";
|
|
13
14
|
export const DEFAULT_SETTINGS = Object.freeze({ concurrency: 8 });
|
|
14
15
|
export function validateCheckpoint(value) {
|
|
@@ -23,11 +24,27 @@ export function validateCheckpoint(value) {
|
|
|
23
24
|
export function workflowSettingsPath(agentDir = getAgentDir()) { return join(agentDir, ROLE_DIRECTORY, "settings.json"); }
|
|
24
25
|
export function workflowProjectSettingsPath(cwd) { return join(cwd, ".pi", ROLE_DIRECTORY, "settings.json"); }
|
|
25
26
|
const EMPTY_AGENT_RESOURCE_EXCLUSIONS = Object.freeze({ skills: [], extensions: [] });
|
|
27
|
+
function resourcePatternHasMagic(value) { return /[*?\x5b\x5d{}()]/.test(value); }
|
|
26
28
|
function normalizedResourcePath(value, settingsPath) {
|
|
27
29
|
let expanded = value === "~" ? homedir() : value.startsWith("~/") || value.startsWith("~\\") ? join(homedir(), value.slice(2)) : value;
|
|
28
30
|
if (expanded.startsWith("file://"))
|
|
29
31
|
expanded = fileURLToPath(expanded);
|
|
30
32
|
const resolved = resolve(dirname(settingsPath), expanded);
|
|
33
|
+
if (expanded === "**" || expanded.startsWith("**/") || expanded.startsWith("**\\"))
|
|
34
|
+
return expanded;
|
|
35
|
+
if (resourcePatternHasMagic(expanded)) {
|
|
36
|
+
const magicIndex = resolved.search(/[*?\x5b\x5d{}()]/);
|
|
37
|
+
const separatorIndex = Math.max(resolved.lastIndexOf("/", magicIndex), resolved.lastIndexOf("\\", magicIndex));
|
|
38
|
+
const rootBoundary = separatorIndex === 0 || (separatorIndex === 2 && /^[A-Za-z]:[\\/]/.test(resolved));
|
|
39
|
+
const prefix = rootBoundary ? resolved.slice(0, separatorIndex + 1) : separatorIndex >= 0 ? resolved.slice(0, separatorIndex) : resolved;
|
|
40
|
+
const suffix = rootBoundary ? resolved.slice(separatorIndex + 1) : separatorIndex >= 0 ? resolved.slice(separatorIndex) : "";
|
|
41
|
+
try {
|
|
42
|
+
return `${realpathSync(prefix)}${suffix}`;
|
|
43
|
+
}
|
|
44
|
+
catch {
|
|
45
|
+
return resolved;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
31
48
|
try {
|
|
32
49
|
return realpathSync(resolved);
|
|
33
50
|
}
|
|
@@ -51,35 +68,41 @@ function validateAgentResourceExclusions(value, settingsPath, errorCode = "INVAL
|
|
|
51
68
|
continue;
|
|
52
69
|
if (!Array.isArray(entries))
|
|
53
70
|
fail(errorCode, `${base}.${kind} must be an array`);
|
|
54
|
-
const seen = new Set();
|
|
55
71
|
for (const [index, entry] of entries.entries()) {
|
|
56
72
|
if (typeof entry !== "string" || !entry.trim())
|
|
57
73
|
fail(errorCode, `${base}.${kind}[${String(index)}] must be a non-empty string`);
|
|
58
74
|
let selector = entry.trim();
|
|
59
75
|
if (kind === "extensions") {
|
|
76
|
+
const negated = selector.startsWith("!");
|
|
77
|
+
const body = negated ? selector.slice(1) : selector;
|
|
78
|
+
if (!body)
|
|
79
|
+
fail(errorCode, `${base}.${kind}[${String(index)}] must be a valid minimatch pattern: Empty minimatch pattern ${JSON.stringify(selector)}`);
|
|
60
80
|
try {
|
|
61
|
-
selector = normalizedResourcePath(
|
|
81
|
+
selector = `${negated ? "!" : ""}${normalizedResourcePath(body, settingsPath)}`;
|
|
62
82
|
}
|
|
63
83
|
catch (error) {
|
|
64
84
|
fail(errorCode, `${base}.${kind}[${String(index)}] must be a valid path: ${errorText(error)}`);
|
|
65
85
|
}
|
|
66
86
|
}
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
87
|
+
try {
|
|
88
|
+
validateResourcePattern(selector);
|
|
89
|
+
}
|
|
90
|
+
catch (error) {
|
|
91
|
+
fail(errorCode, `${base}.${kind}[${String(index)}] must be a valid minimatch pattern: ${errorText(error)}`);
|
|
70
92
|
}
|
|
93
|
+
normalized[kind].push(selector);
|
|
71
94
|
}
|
|
72
95
|
}
|
|
73
96
|
return Object.freeze({ skills: Object.freeze(normalized.skills), extensions: Object.freeze(normalized.extensions) });
|
|
74
97
|
}
|
|
75
|
-
|
|
98
|
+
function parseSettings(path, partial) {
|
|
76
99
|
let parsed;
|
|
77
100
|
try {
|
|
78
101
|
parsed = JSON.parse(readFileSync(path, "utf8"));
|
|
79
102
|
}
|
|
80
103
|
catch (error) {
|
|
81
104
|
if (error.code === "ENOENT")
|
|
82
|
-
return DEFAULT_SETTINGS;
|
|
105
|
+
return partial ? Object.freeze({}) : DEFAULT_SETTINGS;
|
|
83
106
|
fail("CONFIG_ERROR", `Invalid workflow settings JSON at ${path}: ${errorText(error)}`);
|
|
84
107
|
}
|
|
85
108
|
if (!object(parsed))
|
|
@@ -88,19 +111,51 @@ export function loadSettings(path = workflowSettingsPath()) {
|
|
|
88
111
|
const unknown = Object.keys(parsed).find((key) => !allowed.has(key));
|
|
89
112
|
if (unknown)
|
|
90
113
|
fail("INVALID_SETTINGS", `Unknown workflow setting at ${path}: ${unknown}`);
|
|
91
|
-
const concurrency = parsed.concurrency === undefined ? DEFAULT_SETTINGS.concurrency : parsed.concurrency;
|
|
92
|
-
if (!positiveInteger(concurrency) || concurrency > 16)
|
|
114
|
+
const concurrency = parsed.concurrency === undefined ? (partial ? undefined : DEFAULT_SETTINGS.concurrency) : parsed.concurrency;
|
|
115
|
+
if (concurrency !== undefined && (!positiveInteger(concurrency) || concurrency > 16))
|
|
93
116
|
fail("INVALID_SETTINGS", `${path}.concurrency must be an integer from 1 to 16`);
|
|
94
117
|
const modelAliases = parsed.modelAliases === undefined ? undefined : validateModelAliases(parsed.modelAliases, path);
|
|
95
118
|
const disabledAgentResources = validateAgentResourceExclusions(parsed.disabledAgentResources, path);
|
|
96
|
-
return Object.freeze({ concurrency, ...(modelAliases ? {
|
|
119
|
+
return Object.freeze({ ...(concurrency === undefined ? {} : { concurrency }), ...(modelAliases === undefined ? {} : { modelAliases }), ...(disabledAgentResources === undefined ? {} : { disabledAgentResources }) });
|
|
97
120
|
}
|
|
98
|
-
export function
|
|
121
|
+
export function loadSettings(path = workflowSettingsPath()) { return parseSettings(path, false); }
|
|
122
|
+
export function loadSettingsOverrides(path) { return parseSettings(path, true); }
|
|
123
|
+
export function resolveWorkflowSettings(cwd, projectTrusted, globalSettingsPath = workflowSettingsPath()) {
|
|
99
124
|
const projectSettingsPath = workflowProjectSettingsPath(cwd);
|
|
100
|
-
const global = loadSettings(globalSettingsPath)
|
|
101
|
-
const project = projectTrusted ?
|
|
102
|
-
const
|
|
103
|
-
|
|
125
|
+
const global = loadSettings(globalSettingsPath);
|
|
126
|
+
const project = projectTrusted ? loadSettingsOverrides(projectSettingsPath) : Object.freeze({});
|
|
127
|
+
const projectHas = (key) => Object.prototype.hasOwnProperty.call(project, key);
|
|
128
|
+
const sources = {
|
|
129
|
+
concurrency: projectHas("concurrency") ? projectSettingsPath : globalSettingsPath,
|
|
130
|
+
modelAliases: projectHas("modelAliases") ? projectSettingsPath : globalSettingsPath,
|
|
131
|
+
disabledAgentResources: projectHas("disabledAgentResources") ? projectSettingsPath : globalSettingsPath,
|
|
132
|
+
};
|
|
133
|
+
const effective = Object.freeze({
|
|
134
|
+
concurrency: project.concurrency ?? global.concurrency,
|
|
135
|
+
...(projectHas("modelAliases") ? { modelAliases: project.modelAliases } : global.modelAliases === undefined ? {} : { modelAliases: global.modelAliases }),
|
|
136
|
+
...(projectHas("disabledAgentResources") ? { disabledAgentResources: project.disabledAgentResources } : global.disabledAgentResources === undefined ? {} : { disabledAgentResources: global.disabledAgentResources }),
|
|
137
|
+
});
|
|
138
|
+
return { globalSettingsPath, projectSettingsPath, projectTrusted, global, project, effective, sources };
|
|
139
|
+
}
|
|
140
|
+
export function validateModelAliasAvailability(aliases, names, availableModels, knownModels, settingsPath) {
|
|
141
|
+
for (const name of names) {
|
|
142
|
+
try {
|
|
143
|
+
const target = modelCapability(name, aliases, knownModels, settingsPath);
|
|
144
|
+
if (!availableModels.has(target))
|
|
145
|
+
unknownModel(name, target, settingsPath);
|
|
146
|
+
}
|
|
147
|
+
catch (error) {
|
|
148
|
+
throw annotateModelAliasError(error, name);
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
export function resolveAgentResourcePolicy(cwd, projectTrusted, globalSettingsPath = workflowSettingsPath()) {
|
|
153
|
+
const resolved = resolveWorkflowSettings(cwd, projectTrusted, globalSettingsPath);
|
|
154
|
+
const empty = EMPTY_AGENT_RESOURCE_EXCLUSIONS;
|
|
155
|
+
const global = resolved.global.disabledAgentResources ?? empty;
|
|
156
|
+
const project = resolved.project.disabledAgentResources ?? empty;
|
|
157
|
+
const effective = resolved.effective.disabledAgentResources ?? empty;
|
|
158
|
+
return { globalSettingsPath: resolved.globalSettingsPath, projectSettingsPath: resolved.projectSettingsPath, projectTrusted, global, project, effective, unmatchedSkills: [], unmatchedExtensions: [] };
|
|
104
159
|
}
|
|
105
160
|
export function saveModelAliases(path = workflowSettingsPath(), aliases = {}) {
|
|
106
161
|
const normalized = validateModelAliases(aliases, path);
|
|
@@ -175,23 +230,60 @@ export function workflowRoleDirectories(agentDir = getAgentDir()) {
|
|
|
175
230
|
function projectRoleDirectories(root) {
|
|
176
231
|
return [join(root, ROLE_DIRECTORY, "roles")];
|
|
177
232
|
}
|
|
178
|
-
function
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
233
|
+
function roleDirectorySources(dirs) {
|
|
234
|
+
const seen = new Set();
|
|
235
|
+
return dirs.flatMap((value) => {
|
|
236
|
+
const source = typeof value === "string" ? { path: value } : { path: value.path, extension: value.extension };
|
|
237
|
+
if (seen.has(source.path))
|
|
238
|
+
return [];
|
|
239
|
+
seen.add(source.path);
|
|
240
|
+
return [source];
|
|
241
|
+
});
|
|
242
|
+
}
|
|
243
|
+
function roleDirectoryLabel(source, extension = true) {
|
|
244
|
+
return source.extension ? `Extension "${source.extension.headline}" (${source.extension.version})` : extension ? "Registered workflow extension" : "Standard workflow";
|
|
245
|
+
}
|
|
246
|
+
function scanRoleFiles(dirs, extension) {
|
|
247
|
+
const files = [];
|
|
248
|
+
for (const source of roleDirectorySources(dirs)) {
|
|
249
|
+
let entries;
|
|
250
|
+
try {
|
|
251
|
+
entries = readdirSync(source.path, { withFileTypes: true });
|
|
252
|
+
}
|
|
253
|
+
catch (error) {
|
|
254
|
+
if (!extension && error.code === "ENOENT")
|
|
255
|
+
continue;
|
|
256
|
+
fail("INVALID_METADATA", `${roleDirectoryLabel(source, extension)} role directory "${source.path}" could not be scanned: ${errorText(error)}`);
|
|
257
|
+
}
|
|
258
|
+
for (const entry of entries)
|
|
259
|
+
if (entry.isFile() && extname(entry.name) === ".md")
|
|
260
|
+
files.push({ name: basename(entry.name, ".md"), path: join(source.path, entry.name), source });
|
|
183
261
|
}
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
262
|
+
return files.sort((left, right) => left.name.localeCompare(right.name) || left.path.localeCompare(right.path));
|
|
263
|
+
}
|
|
264
|
+
function readRoleDefinitions(dirs, extension = false) {
|
|
265
|
+
const files = scanRoleFiles(dirs, extension);
|
|
266
|
+
if (extension) {
|
|
267
|
+
const byName = new Map();
|
|
268
|
+
for (const file of files)
|
|
269
|
+
byName.set(file.name, [...(byName.get(file.name) ?? []), file]);
|
|
270
|
+
for (const [name, matches] of byName)
|
|
271
|
+
if (matches.length > 1)
|
|
272
|
+
fail("INVALID_METADATA", `Duplicate extension role "${name}": ${matches.map(({ path, source }) => `${roleDirectoryLabel(source)} role directory "${source.path}" (${path})`).join("; ")}`);
|
|
188
273
|
}
|
|
274
|
+
return Object.fromEntries(files.map(({ name, path, source }) => {
|
|
275
|
+
try {
|
|
276
|
+
return [name, parseRoleMarkdown(readFileSync(path, "utf8"), true, path)];
|
|
277
|
+
}
|
|
278
|
+
catch (error) {
|
|
279
|
+
if (extension)
|
|
280
|
+
fail("INVALID_METADATA", `${roleDirectoryLabel(source)} role directory "${source.path}" contains invalid role "${name}" at "${path}": ${errorText(error)}`);
|
|
281
|
+
throw error;
|
|
282
|
+
}
|
|
283
|
+
}));
|
|
189
284
|
}
|
|
190
|
-
function
|
|
191
|
-
return
|
|
192
|
-
}
|
|
193
|
-
export function loadAgentDefinitions(cwd, agentDir = getAgentDir(), projectTrusted = true) {
|
|
194
|
-
return deepFreeze({ ...readRoleDefinitions(workflowRoleDirectories(agentDir)), ...(projectTrusted ? readRoleDefinitions(projectRoleDirectories(join(cwd, ".pi"))) : {}) });
|
|
285
|
+
export function loadAgentDefinitions(cwd, agentDir = getAgentDir(), projectTrusted = true, extensionRoleDirectories = registeredWorkflowRoleDirectoryRegistrations()) {
|
|
286
|
+
return deepFreeze({ ...readRoleDefinitions(extensionRoleDirectories, true), ...readRoleDefinitions(workflowRoleDirectories(agentDir)), ...(projectTrusted ? readRoleDefinitions(projectRoleDirectories(join(cwd, ".pi"))) : {}) });
|
|
195
287
|
}
|
|
196
288
|
function validateRolePolicies(definitions, roles, availableModels, rootTools, aliases = {}, knownModels = availableModels, settingsPath) {
|
|
197
289
|
for (const role of roles) {
|
|
@@ -309,6 +401,39 @@ function workflowCallsWithStructure(program) {
|
|
|
309
401
|
visit(program, { execution: "sequential", structure: [] });
|
|
310
402
|
return calls.sort((left, right) => left.call.start - right.call.start);
|
|
311
403
|
}
|
|
404
|
+
function memberCall(node, objectName, propertyName) {
|
|
405
|
+
if (node?.type !== "CallExpression" || node.callee.type !== "MemberExpression" || node.callee.computed || node.callee.object.type !== "Identifier" || node.callee.object.name !== objectName || node.callee.property.type !== "Identifier")
|
|
406
|
+
return false;
|
|
407
|
+
return node.callee.property.name === propertyName;
|
|
408
|
+
}
|
|
409
|
+
function mapCallback(node) {
|
|
410
|
+
if (!memberCall(node, "Promise", "all") && !memberCall(node, "Promise", "allSettled"))
|
|
411
|
+
return undefined;
|
|
412
|
+
if (node.type !== "CallExpression")
|
|
413
|
+
return undefined;
|
|
414
|
+
const source = node.arguments[0];
|
|
415
|
+
if (source?.type !== "CallExpression" || source.callee.type !== "MemberExpression" || source.callee.computed || source.callee.property.type !== "Identifier" || !["map", "flatMap"].includes(source.callee.property.name))
|
|
416
|
+
return undefined;
|
|
417
|
+
const callback = source.arguments[0];
|
|
418
|
+
return callback?.type === "ArrowFunctionExpression" || callback?.type === "FunctionExpression" ? callback : undefined;
|
|
419
|
+
}
|
|
420
|
+
function hasUnscopedAgent(node, scoped = false) {
|
|
421
|
+
const operation = workflowCallKind(node);
|
|
422
|
+
if (operation === "agent")
|
|
423
|
+
return !scoped;
|
|
424
|
+
const nestedScope = scoped || operation === "parallel" || operation === "pipeline";
|
|
425
|
+
return astChildren(node).some((child) => hasUnscopedAgent(child, nestedScope));
|
|
426
|
+
}
|
|
427
|
+
function validateObviousConcurrentAgentCalls(program) {
|
|
428
|
+
const visit = (node) => {
|
|
429
|
+
const callback = mapCallback(node);
|
|
430
|
+
if (callback && hasUnscopedAgent(callback))
|
|
431
|
+
fail("INVALID_METADATA", "Promise.all/map agent fan-out cannot prove stable call-site identity; use parallel(...) or pipeline(...)");
|
|
432
|
+
for (const child of astChildren(node))
|
|
433
|
+
visit(child);
|
|
434
|
+
};
|
|
435
|
+
visit(program);
|
|
436
|
+
}
|
|
312
437
|
function validateDirectPrimitiveReferences(program, name) {
|
|
313
438
|
const visit = (node, parent) => {
|
|
314
439
|
if (node.type === "Identifier" && node.name === name) {
|
|
@@ -639,6 +764,7 @@ export function preflight(script, capabilities, schemas = [], metadata = { name:
|
|
|
639
764
|
for (const [index, schema] of schemas.entries())
|
|
640
765
|
validateSchema(schema, `schema[${String(index)}]`);
|
|
641
766
|
const calls = workflowCalls(program);
|
|
767
|
+
validateObviousConcurrentAgentCalls(program);
|
|
642
768
|
const phases = calls.filter((call) => call.callee.name === "phase").map((call) => literalString(call.arguments[0])).filter((phase) => phase !== undefined);
|
|
643
769
|
for (const call of calls) {
|
|
644
770
|
const operation = call.callee.name;
|
|
@@ -709,7 +835,7 @@ export function validateWorkflowLaunchWithRegistry(params, context, registry) {
|
|
|
709
835
|
if (!script)
|
|
710
836
|
fail("INVALID_SYNTAX", "Provide script or registered function");
|
|
711
837
|
const metadata = validateWorkflowMetadata({ name: workflowName, ...(typeof params.description === "string" ? { description: params.description } : fn?.description ? { description: fn.description } : {}) });
|
|
712
|
-
const globalAgentDefinitions = loadAgentDefinitions(context.cwd, context.agentDir, false);
|
|
838
|
+
const globalAgentDefinitions = loadAgentDefinitions(context.cwd, context.agentDir, false, registry && typeof registry.roleDirectoryRegistrations === "function" ? registry.roleDirectoryRegistrations() : registry && typeof registry.roleDirectories === "function" ? registry.roleDirectories() : undefined);
|
|
713
839
|
const projectAgentDefinitions = context.projectTrusted ? readRoleDefinitions(projectRoleDirectories(join(context.cwd, ".pi"))) : {};
|
|
714
840
|
const agentDefinitions = deepFreeze({ ...globalAgentDefinitions, ...projectAgentDefinitions });
|
|
715
841
|
const aliases = context.modelAliases ?? {};
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { JsonValue } from "./types.js";
|
|
2
|
+
export interface WorkflowArtifact {
|
|
3
|
+
extension: ".js" | ".json" | ".md";
|
|
4
|
+
content: string;
|
|
5
|
+
}
|
|
6
|
+
export type WorkflowTui = {
|
|
7
|
+
stop(): void;
|
|
8
|
+
start(): void;
|
|
9
|
+
requestRender(force?: boolean): void;
|
|
10
|
+
};
|
|
11
|
+
export declare function workflowScriptArtifact(script: string): WorkflowArtifact;
|
|
12
|
+
export declare function workflowResultArtifact(value: JsonValue): WorkflowArtifact;
|
|
13
|
+
export declare function openWorkflowArtifact(tui: WorkflowTui, command: string, artifact: WorkflowArtifact): Promise<number | null>;
|