pi-extensible-workflows 2.0.0 → 3.1.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.
Files changed (44) hide show
  1. package/README.md +18 -11
  2. package/dist/src/agent-execution.d.ts +4 -94
  3. package/dist/src/agent-execution.js +34 -208
  4. package/dist/src/budget.d.ts +38 -0
  5. package/dist/src/budget.js +160 -0
  6. package/dist/src/cli.d.ts +2 -0
  7. package/dist/src/cli.js +60 -8
  8. package/dist/src/doctor-cleanup.d.ts +41 -0
  9. package/dist/src/doctor-cleanup.js +597 -0
  10. package/dist/src/doctor.d.ts +7 -2
  11. package/dist/src/doctor.js +127 -22
  12. package/dist/src/execution.d.ts +17 -0
  13. package/dist/src/execution.js +630 -0
  14. package/dist/src/host.d.ts +101 -0
  15. package/dist/src/host.js +3084 -0
  16. package/dist/src/index.d.ts +13 -634
  17. package/dist/src/index.js +10 -4432
  18. package/dist/src/persistence.d.ts +9 -19
  19. package/dist/src/persistence.js +175 -61
  20. package/dist/src/registry.d.ts +43 -0
  21. package/dist/src/registry.js +279 -0
  22. package/dist/src/session-inspector.js +5 -3
  23. package/dist/src/types.d.ts +692 -0
  24. package/dist/src/types.js +27 -0
  25. package/dist/src/utils.d.ts +32 -0
  26. package/dist/src/utils.js +168 -0
  27. package/dist/src/validation.d.ts +28 -0
  28. package/dist/src/validation.js +832 -0
  29. package/package.json +3 -2
  30. package/skills/pi-extensible-workflows/SKILL.md +69 -34
  31. package/src/agent-execution.ts +37 -189
  32. package/src/budget.ts +75 -0
  33. package/src/cli.ts +47 -7
  34. package/src/doctor-cleanup.ts +337 -0
  35. package/src/doctor.ts +117 -24
  36. package/src/execution.ts +540 -0
  37. package/src/host.ts +2598 -0
  38. package/src/index.ts +14 -3856
  39. package/src/persistence.ts +122 -37
  40. package/src/registry.ts +240 -0
  41. package/src/session-inspector.ts +5 -3
  42. package/src/types.ts +158 -0
  43. package/src/utils.ts +142 -0
  44. package/src/validation.ts +688 -0
@@ -1,4 +1,4 @@
1
- import type { BudgetApprovalRequest, JsonValue, LaunchSnapshot, RunRecord, WorkflowRunEvent } from "./index.js";
1
+ import type { BudgetApprovalRequest, JsonValue, LaunchSnapshot, RunRecord, WorkflowRunEvent } from "./types.js";
2
2
  import type { OwnershipRecord } from "./agent-execution.js";
3
3
  export interface NativeSessionReference {
4
4
  sessionId: string;
@@ -11,20 +11,6 @@ export interface EffectiveSystemPrompt {
11
11
  sha256: string;
12
12
  prompt: string;
13
13
  }
14
- export interface ConversationHead {
15
- turn: number;
16
- sessionId: string;
17
- sessionFile: string;
18
- leafId: string;
19
- systemPrompt: string;
20
- systemPromptSha256: string;
21
- toolDefinitionsSha256: string;
22
- }
23
- export interface PersistedConversation {
24
- id: string;
25
- policy: JsonValue;
26
- head: ConversationHead;
27
- }
28
14
  export interface PersistedRun extends RunRecord {
29
15
  nativeSessions: readonly NativeSessionReference[];
30
16
  }
@@ -53,7 +39,9 @@ export interface BorrowedWorktreeBinding {
53
39
  owner: string;
54
40
  }
55
41
  export declare function projectStorageKey(cwd: string): string;
42
+ export declare function projectSessionsDirectory(cwd: string, home?: string): string;
56
43
  export declare function runsDirectory(cwd: string, sessionId: string, home?: string): string;
44
+ export declare function hasLiveSessionLease(cwd: string, sessionId: string, home?: string): Promise<boolean>;
57
45
  export declare class SessionLease {
58
46
  #private;
59
47
  readonly path: string;
@@ -80,7 +68,6 @@ export declare class RunStore {
80
68
  private snapshotWrite;
81
69
  private launchSnapshotWrite;
82
70
  private systemPromptWrite;
83
- private conversationWrite;
84
71
  constructor(cwd: string, sessionId: string, runId: string, home?: string);
85
72
  create(run: PersistedRun, snapshot: Readonly<LaunchSnapshot>): Promise<void>;
86
73
  isComplete(): Promise<boolean>;
@@ -97,12 +84,11 @@ export declare class RunStore {
97
84
  systemPromptPath(): string;
98
85
  recordSystemPrompt(entry: Omit<EffectiveSystemPrompt, "sha256">): Promise<void>;
99
86
  systemPrompts(): Promise<readonly EffectiveSystemPrompt[]>;
100
- conversationPath(): string;
101
- conversation(id: string): Promise<PersistedConversation | undefined>;
102
- saveConversation(conversation: PersistedConversation): Promise<void>;
103
87
  private updateJournal;
104
88
  complete(path: string, value: JsonValue): Promise<void>;
105
89
  replay(path: string): Promise<CompletedOperation | undefined>;
90
+ replayableOperations(): Promise<readonly CompletedOperation[]>;
91
+ private replayableOperationsFrom;
106
92
  awaitCheckpoint(checkpoint: AwaitingCheckpoint): Promise<boolean | undefined>;
107
93
  awaitingCheckpoints(): Promise<readonly AwaitingCheckpoint[]>;
108
94
  requestWorkflowDecision(request: PendingWorkflowDecision): Promise<void>;
@@ -119,6 +105,7 @@ export declare class RunStore {
119
105
  private borrowedWorktree;
120
106
  private sourceRun;
121
107
  validateParentRun(parentRunId: string): Promise<void>;
108
+ validateRetrySource(): Promise<void>;
122
109
  private ownedWorktree;
123
110
  private resolveBorrowedWorktree;
124
111
  private findNamedWorktree;
@@ -127,7 +114,9 @@ export declare class RunStore {
127
114
  sourceRunId: string;
128
115
  owner: string;
129
116
  }>;
117
+ validateDeletionWorktrees(): Promise<void>;
130
118
  validateBorrowedWorktrees(): Promise<void>;
119
+ validateNamedWorktrees(): Promise<void>;
131
120
  ownsWorktree(owner: string): Promise<boolean>;
132
121
  private cleanupMarker;
133
122
  private cleanupOrphanWorktrees;
@@ -137,6 +126,7 @@ export declare class RunStore {
137
126
  private bindBorrowedWorktree;
138
127
  snapshotWorktree(owner: string): Promise<string>;
139
128
  worktrees(): Promise<readonly WorktreeReference[]>;
129
+ validNamedWorktrees(): Promise<readonly string[]>;
140
130
  changedWorktrees(): Promise<readonly WorktreeReference[]>;
141
131
  saveResult(value: JsonValue): Promise<string>;
142
132
  delete(confirmed: boolean): Promise<void>;
@@ -5,13 +5,8 @@ import { access, link, mkdir, open, readFile, readdir, rename, rm, stat, writeFi
5
5
  import { basename, dirname, join, relative, resolve, sep } from "node:path";
6
6
  import { homedir } from "node:os";
7
7
  import { promisify } from "node:util";
8
- import { loadLaunchSnapshot, WorkflowError } from "./index.js";
9
- function isConversationArtifact(value) {
10
- if (!value || typeof value !== "object" || Array.isArray(value))
11
- return false;
12
- const artifact = value;
13
- return artifact.version === 1 && Boolean(artifact.conversations) && typeof artifact.conversations === "object" && !Array.isArray(artifact.conversations);
14
- }
8
+ import { WorkflowError } from "./types.js";
9
+ import { loadLaunchSnapshot } from "./utils.js";
15
10
  const execute = promisify(execFile);
16
11
  const gitIdentity = {
17
12
  GIT_AUTHOR_NAME: "pi-extensible-workflows", GIT_AUTHOR_EMAIL: "pi-extensible-workflows@localhost", GIT_COMMITTER_NAME: "pi-extensible-workflows", GIT_COMMITTER_EMAIL: "pi-extensible-workflows@localhost",
@@ -23,8 +18,11 @@ export function projectStorageKey(cwd) {
23
18
  const slug = safePart(basename(exact)) || "root";
24
19
  return `${slug}-${createHash("sha256").update(exact).digest("hex").slice(0, 12)}`;
25
20
  }
21
+ export function projectSessionsDirectory(cwd, home = homedir()) {
22
+ return join(home, ".pi", "workflows", "projects", projectStorageKey(cwd), "sessions");
23
+ }
26
24
  export function runsDirectory(cwd, sessionId, home = homedir()) {
27
- return join(home, ".pi", "workflows", "projects", projectStorageKey(cwd), "sessions", safePart(sessionId), "runs");
25
+ return join(projectSessionsDirectory(cwd, home), safePart(sessionId), "runs");
28
26
  }
29
27
  const SESSION_OWNER_FILE = "owner.json";
30
28
  const SESSION_OWNER_WRITE_GRACE_MS = 30_000;
@@ -48,6 +46,24 @@ async function processAlive(pid, startedAt) {
48
46
  }
49
47
  return true;
50
48
  }
49
+ export async function hasLiveSessionLease(cwd, sessionId, home = homedir()) {
50
+ const path = join(runsDirectory(cwd, sessionId, home), SESSION_OWNER_FILE);
51
+ let owner;
52
+ try {
53
+ owner = JSON.parse(await readFile(path, "utf8"));
54
+ }
55
+ catch (error) {
56
+ if (error.code === "ENOENT")
57
+ return false;
58
+ throw error;
59
+ }
60
+ if (!owner || typeof owner !== "object" || Array.isArray(owner))
61
+ throw new WorkflowError("RUN_OWNED", `Pi session ${sessionId} has an invalid ownership lease`);
62
+ const candidate = owner;
63
+ if (typeof candidate.pid !== "number" || !Number.isInteger(candidate.pid) || candidate.pid < 1 || typeof candidate.token !== "string" || !candidate.token || typeof candidate.startedAt !== "number" || !Number.isFinite(candidate.startedAt))
64
+ throw new WorkflowError("RUN_OWNED", `Pi session ${sessionId} has an invalid ownership lease`);
65
+ return processAlive(candidate.pid, candidate.startedAt);
66
+ }
51
67
  function sameOwner(left, right) {
52
68
  if (!left || typeof left !== "object" || !right || typeof right !== "object")
53
69
  return false;
@@ -226,7 +242,6 @@ export class RunStore {
226
242
  launchSnapshotWrite = Promise.resolve();
227
243
  // ponytail: the session lease prevents concurrent RunStore writers for one run.
228
244
  systemPromptWrite = Promise.resolve();
229
- conversationWrite = Promise.resolve();
230
245
  constructor(cwd, sessionId, runId, home = homedir()) {
231
246
  this.cwd = cwd;
232
247
  this.sessionId = sessionId;
@@ -250,7 +265,6 @@ export class RunStore {
250
265
  await atomicJson(join(temporary, "borrowed-worktrees.json"), []);
251
266
  await atomicJson(join(temporary, "state.json"), run);
252
267
  await atomicJson(join(temporary, "system-prompts.json"), { version: 1, entries: [] });
253
- await atomicJson(join(temporary, "conversations.json"), { version: 1, conversations: {} });
254
268
  await rename(temporary, this.directory);
255
269
  }
256
270
  catch (error) {
@@ -329,54 +343,6 @@ export class RunStore {
329
343
  return (await json(this.systemPromptPath()).catch((error) => { if (error.code === "ENOENT")
330
344
  return { version: 1, entries: [] }; throw error; })).entries;
331
345
  }
332
- conversationPath() { return join(this.directory, "conversations.json"); }
333
- async conversation(id) {
334
- await this.conversationWrite;
335
- let artifact;
336
- try {
337
- const raw = await json(this.conversationPath());
338
- if (!isConversationArtifact(raw))
339
- throw new WorkflowError("RESUME_INCOMPATIBLE", "Conversation state is corrupt");
340
- artifact = raw;
341
- }
342
- catch (error) {
343
- if (error.code === "ENOENT")
344
- return undefined;
345
- if (error instanceof WorkflowError)
346
- throw error;
347
- throw new WorkflowError("RESUME_INCOMPATIBLE", `Cannot load conversation state: ${error instanceof Error ? error.message : String(error)}`);
348
- }
349
- return artifact.conversations[id];
350
- }
351
- async saveConversation(conversation) {
352
- const write = this.conversationWrite.then(async () => {
353
- const path = this.conversationPath();
354
- let artifact;
355
- try {
356
- const raw = await json(path);
357
- if (!isConversationArtifact(raw))
358
- throw new WorkflowError("RESUME_INCOMPATIBLE", "Conversation state is corrupt");
359
- artifact = raw;
360
- }
361
- catch (error) {
362
- if (error.code === "ENOENT")
363
- artifact = { version: 1, conversations: {} };
364
- else if (error instanceof WorkflowError)
365
- throw error;
366
- else
367
- throw new WorkflowError("RESUME_INCOMPATIBLE", `Cannot load conversation state: ${error instanceof Error ? error.message : String(error)}`);
368
- }
369
- const previous = artifact.conversations[conversation.id];
370
- if (previous && previous.head.turn + 1 !== conversation.head.turn)
371
- throw new WorkflowError("RESUME_INCOMPATIBLE", `Conversation head is not the previous turn: ${conversation.id}`);
372
- if (!previous && conversation.head.turn !== 1)
373
- throw new WorkflowError("RESUME_INCOMPATIBLE", `Conversation must start at turn one: ${conversation.id}`);
374
- artifact.conversations[conversation.id] = structuredClone(conversation);
375
- await atomicJson(path, artifact);
376
- });
377
- this.conversationWrite = write.catch(() => undefined);
378
- await write;
379
- }
380
346
  async updateJournal(update) {
381
347
  let result;
382
348
  const write = this.journalWrite.then(async () => {
@@ -398,10 +364,34 @@ export class RunStore {
398
364
  });
399
365
  }
400
366
  async replay(path) {
367
+ const operations = await this.replayableOperations();
368
+ return operations.find((operation) => operation.path === path);
369
+ }
370
+ async replayableOperations() {
371
+ return this.replayableOperationsFrom(new Set());
372
+ }
373
+ async replayableOperationsFrom(seen) {
374
+ if (seen.has(this.runId))
375
+ throw new WorkflowError("RESUME_INCOMPATIBLE", "Retry provenance contains a cycle");
376
+ const nextSeen = new Set(seen);
377
+ nextSeen.add(this.runId);
401
378
  await this.journalWrite;
402
- return (await json(join(this.directory, "journal.json"))).completed[path];
379
+ const loaded = await this.load();
380
+ const operations = new Map();
381
+ if (loaded.run.retry?.sourceRunId) {
382
+ const source = await this.sourceRun(loaded.run.retry.sourceRunId);
383
+ for (const operation of await source.replayableOperationsFrom(nextSeen))
384
+ operations.set(operation.path, operation);
385
+ }
386
+ const journal = await json(join(this.directory, "journal.json"));
387
+ for (const operation of Object.values(journal.completed))
388
+ operations.set(operation.path, operation);
389
+ return [...operations.values()].map((operation) => structuredClone(operation));
403
390
  }
404
391
  async awaitCheckpoint(checkpoint) {
392
+ const replayed = await this.replay(checkpoint.path);
393
+ if (replayed)
394
+ return replayed.value;
405
395
  return this.updateJournal((journal) => {
406
396
  const completed = journal.completed[checkpoint.path];
407
397
  if (completed)
@@ -523,6 +513,36 @@ export class RunStore {
523
513
  }
524
514
  }
525
515
  async validateParentRun(parentRunId) { await this.sourceRun(parentRunId); }
516
+ async validateRetrySource() {
517
+ const validate = async (current, seen) => {
518
+ if (seen.has(current.runId))
519
+ throw new WorkflowError("RESUME_INCOMPATIBLE", "Retry provenance contains a cycle");
520
+ const nextSeen = new Set(seen);
521
+ nextSeen.add(current.runId);
522
+ const loaded = await current.load();
523
+ const retry = loaded.run.retry;
524
+ if (!retry)
525
+ return;
526
+ if (typeof retry.sourceRunId !== "string" || !retry.sourceRunId || retry.sourceRunId === current.runId || typeof retry.lineageRootRunId !== "string" || !retry.lineageRootRunId || !Array.isArray(retry.completedPaths) || retry.completedPaths.some((path) => typeof path !== "string") || !Array.isArray(retry.incompletePaths) || retry.incompletePaths.some((path) => typeof path !== "string") || !Array.isArray(retry.namedWorktrees) || retry.namedWorktrees.some((name) => typeof name !== "string"))
527
+ throw new WorkflowError("RESUME_INCOMPATIBLE", "Retry provenance is incomplete");
528
+ const source = await current.sourceRun(retry.sourceRunId);
529
+ const sourceRun = (await source.load()).run;
530
+ if (loaded.run.parentRunId !== retry.sourceRunId)
531
+ throw new WorkflowError("RESUME_INCOMPATIBLE", "Retry parent run does not match its source run");
532
+ if (sourceRun.state !== "failed")
533
+ throw new WorkflowError("RESUME_INCOMPATIBLE", `Retry source run ${retry.sourceRunId} is not failed`);
534
+ const expectedLineageRoot = sourceRun.retry?.lineageRootRunId ?? sourceRun.id;
535
+ if (retry.lineageRootRunId !== expectedLineageRoot)
536
+ throw new WorkflowError("RESUME_INCOMPATIBLE", "Retry lineage root does not match its source run");
537
+ await validate(source, nextSeen);
538
+ };
539
+ try {
540
+ await validate(this, new Set());
541
+ }
542
+ catch (error) {
543
+ throw error instanceof WorkflowError && error.code === "RESUME_INCOMPATIBLE" ? error : new WorkflowError("RESUME_INCOMPATIBLE", error instanceof Error ? error.message : String(error));
544
+ }
545
+ }
526
546
  async ownedWorktree(owner, cwd) {
527
547
  const records = await json(join(this.directory, "worktrees.json"));
528
548
  const matches = records.filter((candidate) => candidate && typeof candidate === "object" && candidate.owner === owner);
@@ -567,8 +587,13 @@ export class RunStore {
567
587
  }
568
588
  const records = await json(join(this.directory, "worktrees.json"));
569
589
  const matches = records.filter((candidate) => candidate && typeof candidate === "object" && candidate.owner === owner);
570
- if (matches.length === 0)
571
- return undefined;
590
+ if (matches.length === 0) {
591
+ const loaded = await this.load();
592
+ if (loaded.run.parentRunId === undefined)
593
+ return undefined;
594
+ const parent = await this.sourceRun(loaded.run.parentRunId);
595
+ return parent.findNamedWorktree(name, nextSeen);
596
+ }
572
597
  try {
573
598
  const reference = await this.ownedWorktree(owner);
574
599
  return { reference, sourceRunId: this.runId, owner };
@@ -583,6 +608,33 @@ export class RunStore {
583
608
  throw new WorkflowError("WORKTREE_FAILED", `Missing named worktree ${name}`);
584
609
  return resolved;
585
610
  }
611
+ async validateDeletionWorktrees() {
612
+ try {
613
+ const records = await json(join(this.directory, "worktrees.json"));
614
+ if (!Array.isArray(records))
615
+ throw new Error("Worktree records are invalid");
616
+ const owners = new Set();
617
+ const paths = new Set();
618
+ records.forEach((record) => {
619
+ if (!record || typeof record !== "object" || typeof record.owner !== "string")
620
+ throw new Error("Invalid worktree record");
621
+ const owner = record.owner;
622
+ if (owners.has(owner))
623
+ throw new Error(`Duplicate worktree record for ${owner}`);
624
+ owners.add(owner);
625
+ const reference = this.structuralWorktree(owner, record);
626
+ paths.add(resolve(reference.path));
627
+ });
628
+ const entries = await readdir(join(this.directory, "worktrees"), { withFileTypes: true }).catch((error) => { if (error.code === "ENOENT")
629
+ return []; throw error; });
630
+ for (const entry of entries)
631
+ if (!entry.isDirectory() || entry.isSymbolicLink() || !paths.has(resolve(join(this.directory, "worktrees", entry.name))))
632
+ throw new Error(`Unrecorded worktree artifact: ${join(this.directory, "worktrees", entry.name)}`);
633
+ }
634
+ catch (error) {
635
+ throw new WorkflowError("WORKTREE_FAILED", error instanceof Error ? error.message : String(error));
636
+ }
637
+ }
586
638
  async validateBorrowedWorktrees() {
587
639
  try {
588
640
  const loaded = await this.load();
@@ -595,6 +647,19 @@ export class RunStore {
595
647
  throw error instanceof WorkflowError && error.code === "WORKTREE_FAILED" ? error : new WorkflowError("WORKTREE_FAILED", error instanceof Error ? error.message : String(error));
596
648
  }
597
649
  }
650
+ async validateNamedWorktrees() {
651
+ try {
652
+ const records = await json(join(this.directory, "worktrees.json"));
653
+ for (const record of records) {
654
+ const owner = record && typeof record === "object" && typeof record.owner === "string" ? record.owner : undefined;
655
+ if (owner && this.worktreeName(owner))
656
+ await this.validateWorktree(owner);
657
+ }
658
+ }
659
+ catch (error) {
660
+ throw error instanceof WorkflowError && error.code === "WORKTREE_FAILED" ? error : new WorkflowError("WORKTREE_FAILED", error instanceof Error ? error.message : String(error));
661
+ }
662
+ }
598
663
  async ownsWorktree(owner) {
599
664
  const records = await json(join(this.directory, "worktrees.json"));
600
665
  return records.filter((candidate) => candidate && typeof candidate === "object" && candidate.owner === owner).length === 1;
@@ -662,6 +727,8 @@ export class RunStore {
662
727
  return resolved.reference;
663
728
  }
664
729
  }
730
+ if (name && Array.isArray(loaded.run.retry?.namedWorktrees) && loaded.run.retry.namedWorktrees.includes(name))
731
+ throw new WorkflowError("WORKTREE_FAILED", `Missing inherited named worktree ${name}`);
665
732
  const existing = records.find((record) => record.owner === owner);
666
733
  if (existing)
667
734
  return this.validateWorktree(owner);
@@ -775,6 +842,53 @@ export class RunStore {
775
842
  const borrowed = await Promise.all(bindings.map(async (binding) => (await this.resolveBorrowedWorktree(binding, new Set([this.runId]))).reference));
776
843
  return [...owned.filter((record) => record !== undefined), ...borrowed];
777
844
  }
845
+ async validNamedWorktrees() {
846
+ const load = async (path) => {
847
+ try {
848
+ return await json(path);
849
+ }
850
+ catch (error) {
851
+ if (error.code === "ENOENT")
852
+ return [];
853
+ throw error;
854
+ }
855
+ };
856
+ const names = new Set();
857
+ const rawRecords = await load(join(this.directory, "worktrees.json"));
858
+ let bindings;
859
+ try {
860
+ bindings = await this.borrowedWorktreeRecords();
861
+ }
862
+ catch (error) {
863
+ if (error instanceof WorkflowError && error.code === "WORKTREE_FAILED")
864
+ return [];
865
+ throw error;
866
+ }
867
+ const boundOwners = new Set(bindings.map((binding) => binding.owner));
868
+ for (const record of Array.isArray(rawRecords) ? rawRecords : []) {
869
+ if (!record || typeof record !== "object")
870
+ continue;
871
+ const owner = record.owner;
872
+ if (typeof owner !== "string")
873
+ continue;
874
+ const name = this.worktreeName(owner);
875
+ if (!name || owner !== this.namedWorktreeOwner(name) || boundOwners.has(owner))
876
+ continue;
877
+ try {
878
+ await this.ownedWorktree(owner);
879
+ names.add(name);
880
+ }
881
+ catch { /* Do not advertise stale or invalid records. */ }
882
+ }
883
+ for (const binding of bindings) {
884
+ try {
885
+ await this.resolveBorrowedWorktree(binding, new Set([this.runId]));
886
+ names.add(binding.name);
887
+ }
888
+ catch { /* Do not advertise stale inherited records. */ }
889
+ }
890
+ return [...names];
891
+ }
778
892
  async changedWorktrees() {
779
893
  const changed = [];
780
894
  for (const valid of await this.worktrees()) {
@@ -0,0 +1,43 @@
1
+ import type { JsonValue, RegisteredAgentSetupHook, WorkflowCatalog, WorkflowCatalogContext, WorkflowCatalogError, WorkflowCatalogFunction, WorkflowCatalogIndex, WorkflowCatalogModelAlias, WorkflowCatalogVariable, WorkflowExtension, WorkflowFunction, WorkflowFunctionContext, WorkflowJournal, WorkflowModelAlias, WorkflowModelAliasResolverContext, WorkflowRoleDirectoryRegistration, WorkflowVariable } from "./types.js";
2
+ export declare class WorkflowRegistry {
3
+ #private;
4
+ get frozen(): boolean;
5
+ freeze(): void;
6
+ register(extension: WorkflowExtension): void;
7
+ function(name: string): WorkflowFunction;
8
+ functions(): Readonly<Record<string, WorkflowFunction>>;
9
+ catalog(context?: WorkflowCatalogContext): WorkflowCatalog;
10
+ catalogIndex(context?: WorkflowCatalogContext): WorkflowCatalogIndex;
11
+ catalogDetail(name: string, context?: WorkflowCatalogContext): WorkflowCatalogFunction | WorkflowCatalogVariable | WorkflowCatalogModelAlias | WorkflowCatalogError;
12
+ globals(): Readonly<Record<string, {
13
+ name: string;
14
+ }>>;
15
+ invokeFunction(name: string, input: unknown, context: Readonly<WorkflowFunctionContext>, path: string, journal: WorkflowJournal): Promise<JsonValue>;
16
+ variables(): readonly {
17
+ name: string;
18
+ variable: WorkflowVariable;
19
+ }[];
20
+ agentSetupHooks(): readonly RegisteredAgentSetupHook[];
21
+ roleDirectories(): readonly string[];
22
+ roleDirectoryRegistrations(): readonly WorkflowRoleDirectoryRegistration[];
23
+ modelAliases(): readonly {
24
+ name: string;
25
+ version: string;
26
+ headline: string;
27
+ extensionDescription: string;
28
+ resolve: WorkflowModelAlias["resolve"];
29
+ }[];
30
+ resolveModelAliases(context: Readonly<WorkflowModelAliasResolverContext>, shadowed?: ReadonlySet<string>): Promise<Readonly<Record<string, string>>>;
31
+ }
32
+ export type WorkflowRegistryApi = Pick<WorkflowRegistry, "frozen" | "freeze" | "register" | "function" | "functions" | "catalog" | "catalogIndex" | "catalogDetail" | "globals" | "invokeFunction" | "variables" | "modelAliases" | "resolveModelAliases" | "agentSetupHooks" | "roleDirectories" | "roleDirectoryRegistrations">;
33
+ export declare function resetWorkflowRegistry(): void;
34
+ export declare function beginWorkflowExtensionLoading(): void;
35
+ export declare function loadingRegistry(): WorkflowRegistryApi;
36
+ export declare function registerWorkflowExtension(extension: WorkflowExtension): void;
37
+ export declare function workflowCatalog(context?: WorkflowCatalogContext): WorkflowCatalog;
38
+ export declare function workflowCatalogIndex(context?: WorkflowCatalogContext): WorkflowCatalogIndex;
39
+ export declare function workflowCatalogDetail(name: string, context?: WorkflowCatalogContext): WorkflowCatalogFunction | WorkflowCatalogVariable | WorkflowCatalogModelAlias | WorkflowCatalogError;
40
+ export declare function registeredWorkflowFunctions(): Readonly<Record<string, WorkflowFunction>>;
41
+ export declare function registeredWorkflowRoleDirectories(): readonly string[];
42
+ export declare function registeredWorkflowRoleDirectoryRegistrations(): readonly WorkflowRoleDirectoryRegistration[];
43
+ export type { WorkflowCatalog, WorkflowCatalogContext, WorkflowCatalogError, WorkflowCatalogFunction, WorkflowCatalogIndex, WorkflowCatalogIndexFunction, WorkflowCatalogIndexVariable, WorkflowCatalogModelAlias, WorkflowCatalogSettings, WorkflowCatalogVariable, WorkflowRoleDirectoryRegistration } from "./types.js";