@marcoscale98/pi-extensible-workflows 5.14.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 +536 -0
- package/README.md +64 -0
- package/dist/src/agent-execution.d.ts +338 -0
- package/dist/src/agent-execution.js +1633 -0
- package/dist/src/background-widget.d.ts +74 -0
- package/dist/src/background-widget.js +1340 -0
- package/dist/src/budget.d.ts +38 -0
- package/dist/src/budget.js +188 -0
- package/dist/src/changelog.d.ts +6 -0
- package/dist/src/changelog.js +112 -0
- package/dist/src/decoders.d.ts +96 -0
- package/dist/src/decoders.js +629 -0
- package/dist/src/execution.d.ts +19 -0
- package/dist/src/execution.js +749 -0
- package/dist/src/herdr.d.ts +53 -0
- package/dist/src/herdr.js +270 -0
- package/dist/src/host-delivery.d.ts +95 -0
- package/dist/src/host-delivery.js +376 -0
- package/dist/src/host-navigator.d.ts +83 -0
- package/dist/src/host-navigator.js +1087 -0
- package/dist/src/host-phases.d.ts +89 -0
- package/dist/src/host-phases.js +320 -0
- package/dist/src/host-recovery.d.ts +141 -0
- package/dist/src/host-recovery.js +356 -0
- package/dist/src/host-runtime.d.ts +78 -0
- package/dist/src/host-runtime.js +378 -0
- package/dist/src/host-view.d.ts +116 -0
- package/dist/src/host-view.js +868 -0
- package/dist/src/host.d.ts +40 -0
- package/dist/src/host.js +1641 -0
- package/dist/src/index.d.ts +29 -0
- package/dist/src/index.js +13725 -0
- package/dist/src/index.js.map +6 -0
- package/dist/src/io.d.ts +14 -0
- package/dist/src/io.js +82 -0
- package/dist/src/package-identity.d.ts +4 -0
- package/dist/src/package-identity.js +6 -0
- package/dist/src/paths.d.ts +14 -0
- package/dist/src/paths.js +51 -0
- package/dist/src/persistence.d.ts +5 -0
- package/dist/src/persistence.js +5 -0
- package/dist/src/pi-runtime-adapter.d.ts +27 -0
- package/dist/src/pi-runtime-adapter.js +243 -0
- package/dist/src/pi-runtime-runner.d.ts +42 -0
- package/dist/src/pi-runtime-runner.js +505 -0
- package/dist/src/registry.d.ts +48 -0
- package/dist/src/registry.js +379 -0
- package/dist/src/retention.d.ts +17 -0
- package/dist/src/retention.js +195 -0
- package/dist/src/runtime/agent-runner.d.ts +143 -0
- package/dist/src/runtime/agent-runner.js +12 -0
- package/dist/src/runtime/index.d.ts +2 -0
- package/dist/src/runtime/index.js +2 -0
- package/dist/src/runtime/namespace.d.ts +14 -0
- package/dist/src/runtime/namespace.js +1 -0
- package/dist/src/runtime/workflow-result.d.ts +2 -0
- package/dist/src/runtime/workflow-result.js +6 -0
- package/dist/src/session-handoff.d.ts +2 -0
- package/dist/src/session-handoff.js +82 -0
- package/dist/src/session-lease.d.ts +12 -0
- package/dist/src/session-lease.js +180 -0
- package/dist/src/store.d.ts +95 -0
- package/dist/src/store.js +951 -0
- package/dist/src/subagent-manager-handle.d.ts +4 -0
- package/dist/src/subagent-manager-handle.js +6 -0
- package/dist/src/tool-timing.d.ts +12 -0
- package/dist/src/tool-timing.js +33 -0
- package/dist/src/trajectory-contracts.d.ts +8 -0
- package/dist/src/trajectory-contracts.js +9 -0
- package/dist/src/trajectory-host-handle.d.ts +12 -0
- package/dist/src/trajectory-host-handle.js +6 -0
- package/dist/src/trajectory.d.ts +128 -0
- package/dist/src/trajectory.js +594 -0
- package/dist/src/types.d.ts +960 -0
- package/dist/src/types.js +60 -0
- package/dist/src/utils.d.ts +57 -0
- package/dist/src/utils.js +225 -0
- package/dist/src/validation.d.ts +29 -0
- package/dist/src/validation.js +1100 -0
- package/dist/src/workflow-artifacts.d.ts +14 -0
- package/dist/src/workflow-artifacts.js +40 -0
- package/dist/starter/index.d.ts +1 -0
- package/dist/starter/index.js +103 -0
- package/dist/starter/index.js.map +6 -0
- package/dist/starter/review-loop.d.ts +72 -0
- package/dist/starter/review-loop.js +43 -0
- package/dist/starter/roles/developer.md +6 -0
- package/dist/starter/roles/oracle.md +18 -0
- package/dist/starter/roles/researcher.md +20 -0
- package/dist/starter/roles/reviewer.md +19 -0
- package/dist/starter/roles/scout.md +18 -0
- package/dist/subagents/index.d.ts +8 -0
- package/dist/subagents/index.js +8021 -0
- package/dist/subagents/index.js.map +6 -0
- package/dist/subagents/src/contracts.d.ts +173 -0
- package/dist/subagents/src/contracts.js +68 -0
- package/dist/subagents/src/decode.d.ts +15 -0
- package/dist/subagents/src/decode.js +182 -0
- package/dist/subagents/src/index.d.ts +12 -0
- package/dist/subagents/src/index.js +180 -0
- package/dist/subagents/src/manager.d.ts +3 -0
- package/dist/subagents/src/manager.js +1636 -0
- package/dist/subagents/src/navigator.d.ts +5 -0
- package/dist/subagents/src/navigator.js +627 -0
- package/dist/subagents/src/view.d.ts +70 -0
- package/dist/subagents/src/view.js +387 -0
- package/dist/subagents/src/worktree.d.ts +34 -0
- package/dist/subagents/src/worktree.js +68 -0
- package/dist/trajectory/assets/favicon.png +0 -0
- package/dist/trajectory/assets/index.html +772 -0
- package/dist/trajectory/assets/marked.min.js +6 -0
- package/dist/trajectory/assets/morphdom.min.js +1 -0
- package/dist/trajectory/index.d.ts +5 -0
- package/dist/trajectory/index.js +3459 -0
- package/dist/trajectory/index.js.map +6 -0
- package/dist/trajectory/src/assets/favicon.png +0 -0
- package/dist/trajectory/src/assets/index.html +772 -0
- package/dist/trajectory/src/assets/marked.min.js +6 -0
- package/dist/trajectory/src/assets/morphdom.min.js +1 -0
- package/dist/trajectory/src/export.d.ts +25 -0
- package/dist/trajectory/src/export.js +80 -0
- package/dist/trajectory/src/index.d.ts +26 -0
- package/dist/trajectory/src/index.js +732 -0
- package/dist/trajectory/src/server.d.ts +7 -0
- package/dist/trajectory/src/server.js +2534 -0
- package/dist/trajectory/src/server.js.map +6 -0
- package/examples/advanced-patterns/auto-model-router.mjs +78 -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 +105 -0
- package/skills/pi-extensible-workflows/SKILL.md +160 -0
- package/src/agent-execution.ts +1533 -0
- package/src/background-widget.ts +1496 -0
- package/src/budget.ts +109 -0
- package/src/changelog.ts +114 -0
- package/src/decoders.ts +567 -0
- package/src/execution.ts +652 -0
- package/src/herdr.ts +240 -0
- package/src/host-delivery.ts +346 -0
- package/src/host-navigator.ts +871 -0
- package/src/host-phases.ts +264 -0
- package/src/host-recovery.ts +333 -0
- package/src/host-runtime.ts +373 -0
- package/src/host-view.ts +828 -0
- package/src/host.ts +1502 -0
- package/src/index.ts +29 -0
- package/src/io.ts +73 -0
- package/src/package-identity.ts +7 -0
- package/src/paths.ts +49 -0
- package/src/persistence.ts +11 -0
- package/src/pi-runtime-adapter.ts +247 -0
- package/src/pi-runtime-runner.ts +436 -0
- package/src/registry.ts +321 -0
- package/src/retention.ts +178 -0
- package/src/runtime/agent-runner.ts +158 -0
- package/src/runtime/index.ts +2 -0
- package/src/runtime/namespace.ts +16 -0
- package/src/runtime/workflow-result.ts +8 -0
- package/src/session-handoff.ts +78 -0
- package/src/session-lease.ts +125 -0
- package/src/store.ts +832 -0
- package/src/subagent-manager-handle.ts +8 -0
- package/src/tool-timing.ts +47 -0
- package/src/trajectory-contracts.ts +13 -0
- package/src/trajectory-host-handle.ts +16 -0
- package/src/trajectory.ts +543 -0
- package/src/types.ts +374 -0
- package/src/utils.ts +192 -0
- package/src/validation.ts +910 -0
- package/src/workflow-artifacts.ts +35 -0
- package/starter/index.ts +35 -0
- package/starter/prompts/council.md +16 -0
- package/starter/prompts/deep-research.md +16 -0
- package/starter/prompts/oracle.md +14 -0
- package/starter/prompts/parallel-review.md +22 -0
- package/starter/prompts/parallel-scout.md +16 -0
- package/starter/prompts/review-loop.md +14 -0
- package/starter/prompts/review.md +14 -0
- package/starter/prompts/scout.md +14 -0
- package/starter/review-loop.ts +73 -0
- package/starter/roles/developer.md +6 -0
- package/starter/roles/oracle.md +18 -0
- package/starter/roles/researcher.md +20 -0
- package/starter/roles/reviewer.md +19 -0
- package/starter/roles/scout.md +18 -0
- package/subagents/README.md +145 -0
- package/subagents/index.ts +8 -0
- package/subagents/src/contracts.ts +176 -0
- package/subagents/src/decode.ts +158 -0
- package/subagents/src/index.ts +194 -0
- package/subagents/src/manager.ts +1560 -0
- package/subagents/src/navigator.ts +488 -0
- package/subagents/src/view.ts +376 -0
- package/subagents/src/worktree.ts +99 -0
- package/trajectory/index.ts +5 -0
- package/trajectory/src/assets/favicon.png +0 -0
- package/trajectory/src/assets/index.html +772 -0
- package/trajectory/src/assets/marked.min.js +6 -0
- package/trajectory/src/assets/morphdom.min.js +1 -0
- package/trajectory/src/export.ts +85 -0
- package/trajectory/src/index.ts +604 -0
- package/trajectory/src/server.ts +447 -0
package/src/types.ts
ADDED
|
@@ -0,0 +1,374 @@
|
|
|
1
|
+
import type { CreateAgentSessionOptions, InlineExtension, SessionManager, ToolDefinition } from "@earendil-works/pi-coding-agent";
|
|
2
|
+
import type { Static, TSchema } from "typebox";
|
|
3
|
+
export const RUN_STATES = ["queued", "running", "pausing", "paused", "awaiting_input", "completed", "failed", "stopped", "interrupted", "budget_exhausted"] as const;
|
|
4
|
+
export const AGENT_STATES = ["queued", "running", "waiting_for_child", "paused", "retrying", "completed", "failed", "cancelled"] as const;
|
|
5
|
+
export type RunState = (typeof RUN_STATES)[number];
|
|
6
|
+
export type AgentState = (typeof AGENT_STATES)[number];
|
|
7
|
+
// Shared terminal-state vocabularies: a hard-terminal run and a settled agent are decided here once,
|
|
8
|
+
// so display, persistence, and lifecycle code cannot drift apart on what "finished" means.
|
|
9
|
+
export type HardTerminalRunState = "completed" | "failed" | "stopped";
|
|
10
|
+
export const HARD_TERMINAL_RUN_STATES: ReadonlySet<RunState> = new Set<HardTerminalRunState>(["completed", "failed", "stopped"]);
|
|
11
|
+
export function isHardTerminalRunState(state: RunState): state is HardTerminalRunState { return HARD_TERMINAL_RUN_STATES.has(state); }
|
|
12
|
+
// Ended by a stop, a host shutdown, or budget exhaustion rather than by its own failure: a failing execution must not overwrite these with "failed".
|
|
13
|
+
export type ExternallyEndedRunState = "stopped" | "interrupted" | "budget_exhausted";
|
|
14
|
+
export function isExternallyEndedRunState(state: RunState): state is ExternallyEndedRunState { return state === "stopped" || state === "interrupted" || state === "budget_exhausted"; }
|
|
15
|
+
export const SETTLED_AGENT_STATES: ReadonlySet<AgentState> = new Set(["completed", "failed", "cancelled"]);
|
|
16
|
+
export const WORKFLOW_CALL_KINDS = ["agent", "parallel", "pipeline", "checkpoint", "phase", "withWorktree", "shell"] as const;
|
|
17
|
+
export type WorkflowCallKind = (typeof WORKFLOW_CALL_KINDS)[number];
|
|
18
|
+
export const WORKFLOW_RUN_STARTED_EVENT = "workflow:run-started";
|
|
19
|
+
export const WORKFLOW_RUN_RESUMED_EVENT = "workflow:run-resumed";
|
|
20
|
+
export const WORKFLOW_RUN_STATE_CHANGED_EVENT = "workflow:run-state-changed";
|
|
21
|
+
export const WORKFLOW_RUN_COMPLETED_EVENT = "workflow:run-completed";
|
|
22
|
+
export const WORKFLOW_RUN_FAILED_EVENT = "workflow:run-failed";
|
|
23
|
+
export const WORKFLOW_AGENT_STATE_CHANGED_EVENT = "workflow:agent-state-changed";
|
|
24
|
+
export const WORKFLOW_PHASE_CHANGED_EVENT = "workflow:phase-changed";
|
|
25
|
+
export const WORKFLOW_CHECKPOINT_STATE_CHANGED_EVENT = "workflow:checkpoint-state-changed";
|
|
26
|
+
export const WORKFLOW_BUDGET_EVENT = "workflow:budget-event";
|
|
27
|
+
export const WORKFLOW_WORKTREE_CREATED_EVENT = "workflow:worktree-created";
|
|
28
|
+
export const WORKFLOW_BLOCKED_EVENT = "workflow:blocked";
|
|
29
|
+
export const ERROR_CODES = [
|
|
30
|
+
"CONFIG_ERROR", "INVALID_SETTINGS", "INVALID_SYNTAX", "INVALID_METADATA", "DUPLICATE_NAME", "INVALID_SCHEMA", "UNKNOWN_MODEL", "UNKNOWN_TOOL", "UNKNOWN_AGENT_TYPE",
|
|
31
|
+
"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",
|
|
32
|
+
"CANCELLED", "WORKER_UNRESPONSIVE", "WORKTREE_FAILED", "RESUME_INCOMPATIBLE", "BUDGET_EXHAUSTED", "INTERNAL_ERROR",
|
|
33
|
+
] as const;
|
|
34
|
+
|
|
35
|
+
export const THINKING_LEVELS = ["off", "minimal", "low", "medium", "high", "xhigh", "max"] as const;
|
|
36
|
+
export type ThinkingLevel = (typeof THINKING_LEVELS)[number];
|
|
37
|
+
export type WorkflowErrorCode = (typeof ERROR_CODES)[number];
|
|
38
|
+
export type JsonValue = null | boolean | number | string | JsonValue[] | { [key: string]: JsonValue };
|
|
39
|
+
export type JsonSchema = { [key: string]: JsonValue };
|
|
40
|
+
type WorkflowSchema = JsonSchema | TSchema;
|
|
41
|
+
export function roleNameOf(value: unknown): string | undefined {
|
|
42
|
+
return typeof value === "string" ? value : undefined;
|
|
43
|
+
}
|
|
44
|
+
// Extra keys are reserved for extensions and forwarded as JSON to agentOptions; core options remain typed.
|
|
45
|
+
export interface AgentOptions<Schema extends TSchema = never> {
|
|
46
|
+
label?: string;
|
|
47
|
+
model?: string;
|
|
48
|
+
tools?: string[];
|
|
49
|
+
skills?: string[];
|
|
50
|
+
extensions?: string[];
|
|
51
|
+
contextFiles?: ContextFileScope[];
|
|
52
|
+
role?: string;
|
|
53
|
+
outputSchema?: JsonSchema | Schema;
|
|
54
|
+
retries?: number;
|
|
55
|
+
timeoutMs?: number | null;
|
|
56
|
+
[key: string]: JsonValue | NoInfer<Schema>;
|
|
57
|
+
}
|
|
58
|
+
export type ParallelTasks = Record<string, () => JsonValue | Promise<JsonValue>>;
|
|
59
|
+
export type ParallelResult<Tasks extends ParallelTasks> = { [Key in keyof Tasks]: Awaited<ReturnType<Tasks[Key]>> };
|
|
60
|
+
export type PipelineItems = Record<string, JsonValue>;
|
|
61
|
+
export type PipelineStage<Input extends JsonValue, Output extends JsonValue> = (value: Input) => Output | Promise<Output>;
|
|
62
|
+
export type PipelineStages<Input extends JsonValue, Output extends JsonValue> = Record<string, PipelineStage<Input, Output>>;
|
|
63
|
+
export type PipelineResult<Items extends PipelineItems, Output extends JsonValue> = { [Key in keyof Items]: Output };
|
|
64
|
+
export interface ShellOptions { timeoutMs?: number; env?: Record<string, string> }
|
|
65
|
+
export interface ShellResult { exitCode: number | null; stdout: string; stderr: string }
|
|
66
|
+
export const BUDGET_DIMENSIONS = ["tokens", "costUsd", "durationMs", "agentLaunches"] as const;
|
|
67
|
+
export type BudgetDimension = (typeof BUDGET_DIMENSIONS)[number];
|
|
68
|
+
export interface BudgetLimits { soft?: number; hard?: number }
|
|
69
|
+
export type WorkflowBudget = Partial<Record<BudgetDimension, BudgetLimits>>;
|
|
70
|
+
export type WorkflowBudgetPatch = Partial<Record<BudgetDimension, BudgetLimits | { soft?: number | null; hard?: number | null } | null>>;
|
|
71
|
+
export interface WorkflowBudgetUsage { tokens: number; costUsd: number; durationMs: number; agentLaunches: number }
|
|
72
|
+
export const BUDGET_EVENT_TYPES = ["soft_crossed", "hard_overrun", "hard_exhausted", "adjustment_requested", "adjustment_approved", "adjustment_rejected"] as const;
|
|
73
|
+
export type BudgetEventType = (typeof BUDGET_EVENT_TYPES)[number];
|
|
74
|
+
export interface BudgetEvent { type: BudgetEventType; budgetVersion: number; dimensions: readonly BudgetDimension[]; usage: WorkflowBudgetUsage; limits: WorkflowBudget; at: number; proposalId?: string; previous?: WorkflowBudget; proposed?: WorkflowBudget }
|
|
75
|
+
export interface BudgetApprovalRequest { kind: "budget"; proposalId: string; runId: string; consumed: WorkflowBudgetUsage; previous: WorkflowBudget; proposed: WorkflowBudget; budgetVersion: number; foreground?: boolean }
|
|
76
|
+
export interface WorkflowErrorShape { code: WorkflowErrorCode; message: string; failedAt?: string }
|
|
77
|
+
export interface WorkflowEventBase { runId: string; sessionId: string; workflowName: string; cwd: string; runDirectory: string; timestamp: number }
|
|
78
|
+
export type WorkflowRunStartedEvent = WorkflowEventBase;
|
|
79
|
+
export type WorkflowRunResumedEvent = WorkflowEventBase;
|
|
80
|
+
export interface WorkflowRunStateChangedEvent extends WorkflowEventBase { previousState: RunState; state: RunState; reason?: string; errorCode?: WorkflowErrorCode }
|
|
81
|
+
export interface WorkflowRunCompletedEvent extends WorkflowEventBase { resultPath: string }
|
|
82
|
+
export interface WorkflowRunFailedEvent extends WorkflowEventBase { error: WorkflowErrorShape }
|
|
83
|
+
export interface WorkflowAgentStateChangedEvent extends WorkflowEventBase { agentId: string; displayLabel: string; role?: string; structuralPath: readonly string[]; parentId?: string; parentBreadcrumb?: string; worktreeOwner?: string; previousState?: AgentState; state: AgentState; attempt: number }
|
|
84
|
+
export interface WorkflowPhaseChangedEvent extends WorkflowEventBase { previousPhase?: string; phase: string }
|
|
85
|
+
export type WorkflowCheckpointState = "awaiting" | "approved" | "rejected";
|
|
86
|
+
export interface WorkflowCheckpointStateChangedEvent extends WorkflowEventBase { name: string; state: WorkflowCheckpointState }
|
|
87
|
+
export interface WorkflowBudgetEvent extends WorkflowEventBase { type: BudgetEventType; budgetVersion: number; dimensions: readonly BudgetDimension[]; usage: WorkflowBudgetUsage; limits: WorkflowBudget; proposalId?: string; previous?: WorkflowBudget; proposed?: WorkflowBudget }
|
|
88
|
+
export interface ModelSpec { provider: string; model: string; thinking?: ThinkingLevel }
|
|
89
|
+
export interface WorkflowModelAliasResolverContext { cwd: string; projectTrusted: boolean; rootModel: ModelSpec; knownModels: ReadonlySet<string>; availableModels: ReadonlySet<string>; signal: AbortSignal }
|
|
90
|
+
export interface WorkflowModelAlias { resolve: (context: Readonly<WorkflowModelAliasResolverContext>) => string | Promise<string> }
|
|
91
|
+
export interface WorkflowMetadata { name: string; description?: string }
|
|
92
|
+
export interface HerdrExtensionSettings { enableFullyInspectableMode?: boolean }
|
|
93
|
+
export interface TrajectoryExtensionSettings { port?: number; themes?: boolean }
|
|
94
|
+
export interface WorkflowExtensionSettings { readonly [namespace: string]: JsonValue; readonly herdr?: Readonly<HerdrExtensionSettings>; readonly trajectory?: Readonly<TrajectoryExtensionSettings> }
|
|
95
|
+
export type WorkflowExtensionSettingsSource = "global" | "project" | "role" | "effective";
|
|
96
|
+
export interface WorkflowExtensionSettingsValidatorContext { source: WorkflowExtensionSettingsSource; cwd: string; projectTrusted: boolean; settingsPath?: string; role?: string }
|
|
97
|
+
export type WorkflowExtensionSettingsValidator = (settings: Readonly<WorkflowExtensionSettings>, context: Readonly<WorkflowExtensionSettingsValidatorContext>) => void;
|
|
98
|
+
export interface WorkflowSessionStartEvent { type: "session_start"; reason: "startup" | "reload" | "new" | "resume" | "fork"; previousSessionFile?: string; settings: Readonly<WorkflowExtensionSettings> }
|
|
99
|
+
export interface WorkflowRetentionSettings { olderThanDays?: number; maxTerminalRuns?: number }
|
|
100
|
+
export interface WorkflowSettings { concurrency: number; backgroundWidget?: boolean; modelAliases?: Readonly<Record<string, string>>; skills?: readonly string[]; extensions?: readonly string[]; extensionSettings?: Readonly<WorkflowExtensionSettings>; tools?: readonly string[]; retention?: Readonly<WorkflowRetentionSettings>; worktreePostCreateCommand?: readonly string[] }
|
|
101
|
+
export type WorkflowSettingsOverrides = Partial<Omit<WorkflowSettings, "backgroundWidget">>;
|
|
102
|
+
export interface WorkflowSettingsSources { concurrency: string; modelAliases: string; skills?: string; extensions?: string; tools?: string; extensionSettings?: string; retention?: string; worktreePostCreateCommand?: string }
|
|
103
|
+
export interface WorkflowSettingsResolution { globalSettingsPath: string; projectSettingsPath: string; projectTrusted: boolean; global: Readonly<WorkflowSettings>; project: Readonly<WorkflowSettingsOverrides>; effective: Readonly<WorkflowSettings>; sources: Readonly<WorkflowSettingsSources> }
|
|
104
|
+
export interface AgentResourceSelectors { skills?: readonly string[]; extensions?: readonly string[]; tools?: readonly string[] }
|
|
105
|
+
export interface AgentResourceSelectorSet { skills: readonly string[]; extensions: readonly string[]; tools?: readonly string[] }
|
|
106
|
+
export interface AgentResourceSelectorSources { global: AgentResourceSelectors; project: AgentResourceSelectors; role?: AgentResourceSelectors; call?: AgentResourceSelectors }
|
|
107
|
+
export const CONTEXT_FILE_SCOPES = ["global", "project", "cwd"] as const;
|
|
108
|
+
export type ContextFileScope = (typeof CONTEXT_FILE_SCOPES)[number];
|
|
109
|
+
export function isContextFileScope(value: unknown): value is ContextFileScope { return CONTEXT_FILE_SCOPES.some((scope) => scope === value); }
|
|
110
|
+
export interface AgentResourcePolicy {
|
|
111
|
+
globalSettingsPath: string;
|
|
112
|
+
projectSettingsPath: string;
|
|
113
|
+
projectTrusted: boolean;
|
|
114
|
+
global: AgentResourceSelectorSet;
|
|
115
|
+
project: AgentResourceSelectorSet;
|
|
116
|
+
effective: AgentResourceSelectorSet;
|
|
117
|
+
selectedSkills?: readonly string[];
|
|
118
|
+
selectedExtensions?: readonly string[];
|
|
119
|
+
selectedTools?: readonly string[];
|
|
120
|
+
unmatchedSkills: readonly string[];
|
|
121
|
+
unmatchedExtensions: readonly string[];
|
|
122
|
+
unmatchedTools?: readonly string[];
|
|
123
|
+
selectorSources: AgentResourceSelectorSources;
|
|
124
|
+
}
|
|
125
|
+
export interface AgentResourceInspection { selectors: AgentResourceSelectorSet; skills: readonly string[]; extensions: readonly string[]; tools: readonly string[]; unmatchedSkills: readonly string[]; unmatchedExtensions: readonly string[]; unmatchedTools: readonly string[]; selectorSources?: AgentResourceSelectorSources }
|
|
126
|
+
|
|
127
|
+
export interface ContextFile { readonly path: string; readonly content: string }
|
|
128
|
+
export interface AgentActivity { kind: "reasoning" | "tool" | "text"; text: string }
|
|
129
|
+
export const WORKFLOW_AGENT_STALL_THRESHOLD_MS = 10 * 60 * 1000;
|
|
130
|
+
export interface AgentAccounting { input: number; output: number; cacheRead: number; cacheWrite: number; cost: number }
|
|
131
|
+
export function zeroAccounting(): AgentAccounting { return { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, cost: 0 }; }
|
|
132
|
+
export function addAccounting(left: AgentAccounting, right: AgentAccounting): AgentAccounting { return { input: left.input + right.input, output: left.output + right.output, cacheRead: left.cacheRead + right.cacheRead, cacheWrite: left.cacheWrite + right.cacheWrite, cost: left.cost + right.cost }; }
|
|
133
|
+
export function sumAccounting(values: Iterable<AgentAccounting>): AgentAccounting {
|
|
134
|
+
let total = zeroAccounting();
|
|
135
|
+
for (const value of values) total = addAccounting(total, value);
|
|
136
|
+
return total;
|
|
137
|
+
}
|
|
138
|
+
export interface AgentSetupSummary { hookNames: readonly string[]; model: ModelSpec; tools: readonly string[]; cwd: string; resourceSelectors?: AgentResourceInspection }
|
|
139
|
+
|
|
140
|
+
export interface AgentAttemptError { code: string; message: string }
|
|
141
|
+
export interface AgentAttemptSummary { attempt: number; transport: string; session?: WorkflowAgentSessionReference; setup: AgentSetupSummary; error?: AgentAttemptError; accounting: AgentAccounting }
|
|
142
|
+
export interface WorkflowWorktreeCreatedEvent extends WorkflowEventBase { owner: string; branch: string; path: string; base: string }
|
|
143
|
+
export interface WorkflowWorktreeReference { readonly path: string; readonly branch: string }
|
|
144
|
+
export interface AgentRecord {
|
|
145
|
+
systemPrompt?: string;
|
|
146
|
+
prompt?: string;
|
|
147
|
+
id: string;
|
|
148
|
+
name: string;
|
|
149
|
+
label?: string;
|
|
150
|
+
path: string;
|
|
151
|
+
state: AgentState;
|
|
152
|
+
parentId?: string;
|
|
153
|
+
structuralPath?: readonly string[];
|
|
154
|
+
resultPath?: string;
|
|
155
|
+
parentBreadcrumb?: string;
|
|
156
|
+
worktreeOwner?: string;
|
|
157
|
+
handle?: string;
|
|
158
|
+
turn?: number;
|
|
159
|
+
continuity?: AgentContinuity;
|
|
160
|
+
role?: string;
|
|
161
|
+
requestedModel?: string;
|
|
162
|
+
model: ModelSpec;
|
|
163
|
+
tools: readonly string[];
|
|
164
|
+
toolDefinitions?: readonly { readonly name: string; readonly description: string }[];
|
|
165
|
+
attempts: number;
|
|
166
|
+
startedAt?: number;
|
|
167
|
+
durationMs?: number;
|
|
168
|
+
attemptDetails?: readonly AgentAttemptSummary[];
|
|
169
|
+
accounting?: AgentAccounting;
|
|
170
|
+
toolCalls?: readonly { id: string; name: string; state: "running" | "completed" | "failed" }[];
|
|
171
|
+
activity?: AgentActivity | undefined;
|
|
172
|
+
lastEventAt?: number;
|
|
173
|
+
}
|
|
174
|
+
export type WorkflowDeliveryMode = "foreground" | "background";
|
|
175
|
+
export type WorkflowDeliveryStatus = "attached" | "pending" | "delivered";
|
|
176
|
+
export interface WorkflowRunDelivery { mode: WorkflowDeliveryMode; state: WorkflowDeliveryStatus; toolCallId?: string }
|
|
177
|
+
export interface WorkflowRunEvent { type: string; message: string; timestamp?: number }
|
|
178
|
+
export interface WorkflowRetryProvenance { sourceRunId: string; lineageRootRunId: string; completedPaths: readonly string[]; incompletePaths: readonly string[]; namedWorktrees: readonly string[] }
|
|
179
|
+
export interface WorkflowPhaseRecord { phase: string; afterAgent: number }
|
|
180
|
+
export interface WorkflowPhaseShellActivity { phaseIndex: number; active: number; startedAt: number }
|
|
181
|
+
export interface RunRecord {
|
|
182
|
+
id: string;
|
|
183
|
+
workflowName: string;
|
|
184
|
+
cwd: string;
|
|
185
|
+
sessionId: string;
|
|
186
|
+
state: RunState;
|
|
187
|
+
agentSessions: readonly WorkflowAgentSessionReference[];
|
|
188
|
+
parentRunId?: string;
|
|
189
|
+
retry?: WorkflowRetryProvenance;
|
|
190
|
+
phase?: string;
|
|
191
|
+
phaseHistory?: readonly WorkflowPhaseRecord[];
|
|
192
|
+
phaseHistoryIndex?: number;
|
|
193
|
+
agents: readonly AgentRecord[];
|
|
194
|
+
activeShells?: number;
|
|
195
|
+
activeShellStartedAt?: number;
|
|
196
|
+
activeShellsByPhase?: readonly WorkflowPhaseShellActivity[];
|
|
197
|
+
error?: WorkflowErrorShape;
|
|
198
|
+
failedAt?: string;
|
|
199
|
+
budget?: WorkflowBudget;
|
|
200
|
+
budgetVersion?: number;
|
|
201
|
+
usage?: WorkflowBudgetUsage;
|
|
202
|
+
budgetEvents?: readonly BudgetEvent[];
|
|
203
|
+
events?: readonly WorkflowRunEvent[];
|
|
204
|
+
delivery?: WorkflowRunDelivery;
|
|
205
|
+
}
|
|
206
|
+
export const LAUNCH_SNAPSHOT_IDENTITY_VERSION = 5;
|
|
207
|
+
export type WorkflowLaunchMode = "foreground" | "background";
|
|
208
|
+
export interface AgentDefinition { prompt?: string; description?: string; model?: string; thinking?: NonNullable<ModelSpec["thinking"]>; tools?: readonly string[]; skills?: readonly string[]; extensions?: readonly string[]; overrideSystemPrompt?: boolean; contextFiles?: readonly ContextFileScope[]; extensionSettings?: Readonly<WorkflowExtensionSettings> }
|
|
209
|
+
export interface LaunchSnapshot {
|
|
210
|
+
identityVersion?: number;
|
|
211
|
+
launchMode?: WorkflowLaunchMode;
|
|
212
|
+
script: string;
|
|
213
|
+
args: JsonValue;
|
|
214
|
+
metadata: WorkflowMetadata;
|
|
215
|
+
settings: WorkflowSettings;
|
|
216
|
+
settingsSources?: WorkflowSettingsSources;
|
|
217
|
+
budget?: WorkflowBudget;
|
|
218
|
+
settingsPath?: string;
|
|
219
|
+
modelAliases?: Readonly<Record<string, string>>;
|
|
220
|
+
phases?: readonly string[];
|
|
221
|
+
models: readonly string[];
|
|
222
|
+
tools: readonly string[];
|
|
223
|
+
agentTypes: readonly string[];
|
|
224
|
+
roles?: Readonly<Record<string, AgentDefinition>>;
|
|
225
|
+
projectRoles?: readonly string[];
|
|
226
|
+
schemas: readonly JsonSchema[];
|
|
227
|
+
}
|
|
228
|
+
export interface PreflightCapabilities { models: ReadonlySet<string>; tools: ReadonlySet<string>; agentTypes: ReadonlySet<string>; modelAliases?: Readonly<Record<string, string>>; knownModels?: ReadonlySet<string>; settingsPath?: string; skipModelAvailability?: boolean }
|
|
229
|
+
export interface PreflightResult { metadata: WorkflowMetadata; referenced: { phases: readonly string[]; models: readonly string[]; tools: readonly string[]; agentTypes: readonly string[] }; schemas: readonly JsonSchema[]; dynamicAgentRoles: boolean }
|
|
230
|
+
export interface WorkflowOrchestrationContext { agent: { <Schema extends TSchema>(prompt: string, options: Readonly<AgentOptions<Schema> & { outputSchema: Schema }>): Promise<Static<Schema>>; (prompt: string, options?: Readonly<AgentOptions>): Promise<JsonValue> }; shell: (command: string, options?: ShellOptions) => Promise<ShellResult>; prompt: (template: string, values: Readonly<Record<string, JsonValue>>) => string; parallel: <Tasks extends ParallelTasks>(operationName: string, tasks: Tasks) => Promise<ParallelResult<Tasks>>; pipeline: <Items extends PipelineItems, Output extends JsonValue>(operationName: string, items: Items, stages: PipelineStages<Items[keyof Items], Output>) => Promise<PipelineResult<Items, Output>>; withWorktree: <Result extends JsonValue>(name: string, callback: WorkflowWorktreeCallback<Result>) => Promise<Result>; checkpoint: (input: CheckpointInput) => Promise<boolean>; phase: (name: string) => void; log: (message: string) => void }
|
|
231
|
+
export interface WorkflowRunContext { cwd: string; sessionId: string; runId: string; workflow: Readonly<WorkflowMetadata>; args: JsonValue; signal: AbortSignal }
|
|
232
|
+
export interface WorkflowFunctionContext extends WorkflowOrchestrationContext { run: Readonly<WorkflowRunContext>; settings: Readonly<WorkflowExtensionSettings>; invoke: (name: string, input: Readonly<Record<string, JsonValue>>, label?: string) => Promise<JsonValue> }
|
|
233
|
+
export type WorkflowWorktreeCallback<Result extends JsonValue = JsonValue> = (reference: Readonly<WorkflowWorktreeReference>) => Result | Promise<Result>;
|
|
234
|
+
export interface WorkflowFunction { description: string; input: WorkflowSchema; output: WorkflowSchema; run(input: Readonly<Record<string, JsonValue>>, context: Readonly<WorkflowFunctionContext>): unknown }
|
|
235
|
+
type TypedWorkflowFunction<InputSchema extends TSchema, OutputSchema extends TSchema> = {
|
|
236
|
+
description: string;
|
|
237
|
+
input: InputSchema;
|
|
238
|
+
output: OutputSchema;
|
|
239
|
+
run: (input: Readonly<Static<NoInfer<InputSchema>>>, context: Readonly<WorkflowFunctionContext>) => Promise<Static<NoInfer<OutputSchema>>> | Static<NoInfer<OutputSchema>>;
|
|
240
|
+
};
|
|
241
|
+
type DefinedWorkflowFunction<InputSchema extends TSchema, OutputSchema extends TSchema, Run extends TypedWorkflowFunction<InputSchema, OutputSchema>["run"]> = Omit<TypedWorkflowFunction<InputSchema, OutputSchema>, "run"> & { run: Run };
|
|
242
|
+
export function defineWorkflowFunction<InputSchema extends TSchema, OutputSchema extends TSchema, Run extends TypedWorkflowFunction<InputSchema, OutputSchema>["run"]>(workflowFunction: DefinedWorkflowFunction<InputSchema, OutputSchema, Run>): DefinedWorkflowFunction<InputSchema, OutputSchema, Run> { return workflowFunction; }
|
|
243
|
+
export interface WorkflowAgentSessionReference { readonly transport: string; readonly sessionId: string; readonly locator?: JsonValue }
|
|
244
|
+
export interface WorkflowAgentSessionStats { readonly tokens: { readonly input: number; readonly output: number; readonly cacheRead: number; readonly cacheWrite: number; readonly total: number }; readonly cost: number }
|
|
245
|
+
export interface WorkflowAgentMessage { readonly role: string; readonly content?: unknown; readonly stopReason?: string; readonly errorMessage?: string; readonly usage?: { readonly input: number; readonly output: number; readonly cacheRead: number; readonly cacheWrite: number; readonly cost: { readonly total: number } } }
|
|
246
|
+
export interface WorkflowAgentSessionState { readonly model: ModelSpec; readonly thinking?: ModelSpec["thinking"]; readonly tools: readonly string[]; readonly systemPrompt?: string }
|
|
247
|
+
export interface WorkflowAgentSessionEvent { readonly type: string; readonly state?: Readonly<WorkflowAgentSessionState>; readonly message?: WorkflowAgentMessage; readonly assistantMessageEvent?: { readonly type: string; readonly delta?: string }; readonly toolCallId?: string; readonly toolName?: string; readonly isError?: boolean }
|
|
248
|
+
export type LiveSessionHandoffState = "local-running" | "handoff-pending" | "herdr-running" | "returning-local" | "completed";
|
|
249
|
+
export interface LiveSessionHandoff {
|
|
250
|
+
readonly state: LiveSessionHandoffState;
|
|
251
|
+
readonly transferred: boolean;
|
|
252
|
+
observe(event: WorkflowAgentSessionEvent): void;
|
|
253
|
+
request(launch: () => Promise<void>): Promise<void>;
|
|
254
|
+
waitForTakeover(): Promise<void>;
|
|
255
|
+
takeover(): void;
|
|
256
|
+
waitForResume(): Promise<void>;
|
|
257
|
+
release(reason?: string): void;
|
|
258
|
+
}
|
|
259
|
+
export interface WorkflowAgentTurnResult { readonly assistant?: WorkflowAgentMessage }
|
|
260
|
+
export interface WorkflowAgentSession {
|
|
261
|
+
readonly reference: WorkflowAgentSessionReference;
|
|
262
|
+
getState(): Readonly<WorkflowAgentSessionState>;
|
|
263
|
+
getSessionStats(): WorkflowAgentSessionStats;
|
|
264
|
+
getLastAssistant(): WorkflowAgentMessage | undefined;
|
|
265
|
+
subscribe(listener: (event: WorkflowAgentSessionEvent) => void): () => void;
|
|
266
|
+
subscribeAsync?(listener: (event: WorkflowAgentSessionEvent) => void | Promise<void>): () => void;
|
|
267
|
+
prompt(text: string): Promise<WorkflowAgentTurnResult>;
|
|
268
|
+
steer(text: string): Promise<void>;
|
|
269
|
+
abort(): Promise<void>;
|
|
270
|
+
suspendForHandoff?(): Promise<void>;
|
|
271
|
+
resumeFromHandoff?(): Promise<void>;
|
|
272
|
+
dispose(): Promise<void>;
|
|
273
|
+
getResourceInspection?(): { readonly skills: readonly string[]; readonly extensions: readonly string[] };
|
|
274
|
+
}
|
|
275
|
+
type SessionTools = NonNullable<CreateAgentSessionOptions["tools"]>;
|
|
276
|
+
type SessionCustomTools = NonNullable<CreateAgentSessionOptions["customTools"]>;
|
|
277
|
+
export interface SessionInput {
|
|
278
|
+
cwd: string;
|
|
279
|
+
model: ModelSpec;
|
|
280
|
+
tools: SessionTools;
|
|
281
|
+
sessionLabel: string;
|
|
282
|
+
sessionPath?: string;
|
|
283
|
+
sessionManager?: SessionManager;
|
|
284
|
+
agentDir?: string;
|
|
285
|
+
customTools?: SessionCustomTools;
|
|
286
|
+
resultTool?: ToolDefinition;
|
|
287
|
+
systemPrompt?: string;
|
|
288
|
+
systemPromptAppend?: string;
|
|
289
|
+
extensionFactories?: InlineExtension[];
|
|
290
|
+
additionalSkillPaths?: readonly string[];
|
|
291
|
+
contextFiles?: readonly ContextFileScope[];
|
|
292
|
+
resourcePolicy?: AgentResourcePolicy;
|
|
293
|
+
readonly settings?: Readonly<WorkflowExtensionSettings>;
|
|
294
|
+
options?: AgentOptions;
|
|
295
|
+
}
|
|
296
|
+
export interface PiRuntimeLaunchInfo {
|
|
297
|
+
readonly executable: string;
|
|
298
|
+
readonly entrypoint?: string;
|
|
299
|
+
}
|
|
300
|
+
export interface PreparedAgentSession {
|
|
301
|
+
readonly cwd: string;
|
|
302
|
+
readonly model: ModelSpec;
|
|
303
|
+
readonly tools: readonly string[];
|
|
304
|
+
readonly sessionLabel: string;
|
|
305
|
+
readonly sessionPath?: string;
|
|
306
|
+
readonly initialPrompt?: string;
|
|
307
|
+
readonly agentDir?: string;
|
|
308
|
+
readonly customTools?: readonly ToolDefinition[];
|
|
309
|
+
readonly resultTool?: ToolDefinition;
|
|
310
|
+
readonly options?: Readonly<Record<string, JsonValue>>;
|
|
311
|
+
readonly piRuntime?: Readonly<PiRuntimeLaunchInfo>;
|
|
312
|
+
readonly piRuntimeError?: string;
|
|
313
|
+
readonly systemPrompt?: string;
|
|
314
|
+
readonly systemPromptPath?: string;
|
|
315
|
+
readonly systemPromptAppend?: string;
|
|
316
|
+
readonly extensionFactories?: readonly InlineExtension[];
|
|
317
|
+
readonly additionalSkillPaths?: readonly string[];
|
|
318
|
+
readonly contextFiles?: readonly ContextFileScope[];
|
|
319
|
+
readonly resourcePolicy?: Readonly<AgentResourcePolicy>;
|
|
320
|
+
readonly settings?: Readonly<WorkflowExtensionSettings>;
|
|
321
|
+
}
|
|
322
|
+
export type AgentInspectionMode = "execution" | "inspection";
|
|
323
|
+
export interface AgentTransportContext { readonly run: Readonly<WorkflowRunContext>; readonly identity: Readonly<AgentIdentity>; readonly attempt: number; readonly signal: AbortSignal; readonly tuiIndex?: number; readonly tuiLabel?: string; readonly settings: Readonly<WorkflowExtensionSettings> }
|
|
324
|
+
export interface AgentTransport { readonly id: string; createSession(prepared: Readonly<PreparedAgentSession>, context: Readonly<AgentTransportContext>): Promise<WorkflowAgentSession> }
|
|
325
|
+
export interface AgentSetup { prompt: string; options: AgentOptions; sessionInput: SessionInput; prepared: Readonly<PreparedAgentSession>; transport: AgentTransport }
|
|
326
|
+
export interface AgentSetupContext { readonly run: Readonly<WorkflowRunContext>; readonly identity: Readonly<AgentIdentity>; readonly attempt: number; readonly signal: AbortSignal; readonly tuiIndex?: number; readonly tuiLabel?: string; readonly mode?: AgentInspectionMode; readonly settings: Readonly<WorkflowExtensionSettings> }
|
|
327
|
+
|
|
328
|
+
export interface AgentSetupHook { priority?: number; setup: (agent: AgentSetup, context: Readonly<AgentSetupContext>) => void | Promise<void> }
|
|
329
|
+
export interface RegisteredAgentSetupHook { name: string; priority: number; setup: AgentSetupHook["setup"] }
|
|
330
|
+
export interface WorkflowExtensionMetadata { version: string; headline: string }
|
|
331
|
+
export interface WorkflowFunctionSource { readonly module: string; readonly export: string; readonly dependencies: readonly string[] }
|
|
332
|
+
export interface WorkflowRoleDirectoryRegistration { path: string; extension: WorkflowExtensionMetadata; readonly builtin?: true }
|
|
333
|
+
export interface AgentAttemptActionUi { notify(message: string, level?: "info" | "warning" | "error"): void; confirm(title: string, message: string): Promise<boolean>; select(title: string, options: readonly string[]): Promise<string | undefined>; input(title: string, placeholder?: string): Promise<string | undefined>; setWorkingMessage?(message?: string): void }
|
|
334
|
+
export interface StandaloneAgentRecord { readonly id: string; readonly name: string; readonly label?: string; readonly state: "running" | "completed" | "failed" | "stopped"; readonly structuralPath?: readonly string[] }
|
|
335
|
+
export interface StandaloneAgentAttemptActionContext { readonly agent: Readonly<StandaloneAgentRecord>; readonly attempt: Readonly<AgentAttemptSummary>; readonly session?: WorkflowAgentSessionReference; readonly liveSession?: WorkflowAgentSession; readonly prepared?: Readonly<PreparedAgentSession>; readonly handoff?: LiveSessionHandoff; readonly signal: AbortSignal; readonly ui: Readonly<AgentAttemptActionUi> }
|
|
336
|
+
export interface AgentAttemptActionContext { readonly run: Readonly<RunRecord>; readonly agent: Readonly<AgentRecord>; readonly attempt: Readonly<AgentAttemptSummary>; readonly session?: WorkflowAgentSessionReference; readonly liveSession?: WorkflowAgentSession; readonly prepared?: Readonly<PreparedAgentSession>; readonly handoff?: LiveSessionHandoff; readonly signal: AbortSignal; readonly ui: Readonly<AgentAttemptActionUi> }
|
|
337
|
+
export interface AgentAttemptAction { readonly label: string; visible(context: Readonly<AgentAttemptActionContext>): boolean; run(context: Readonly<AgentAttemptActionContext>): void | Promise<void>; visibleStandalone?(context: Readonly<StandaloneAgentAttemptActionContext>): boolean; runStandalone?(context: Readonly<StandaloneAgentAttemptActionContext>): void | Promise<void> }
|
|
338
|
+
export interface WorkflowExtension extends WorkflowExtensionMetadata { description?: string; source?: string; dependencies?: readonly string[]; validateSettings?: WorkflowExtensionSettingsValidator; functions?: Readonly<Record<string, WorkflowFunction>>; modelAliases?: Readonly<Record<string, WorkflowModelAlias>>; agentSetupHooks?: Readonly<Record<string, AgentSetupHook>>; agentAttemptActions?: Readonly<Record<string, AgentAttemptAction>>; roleDirectories?: readonly (string | URL)[] }
|
|
339
|
+
export interface WorkflowJournal { get(path: string): JsonValue | undefined; put(path: string, value: JsonValue): void }
|
|
340
|
+
// The brand keeps instanceof working across the bundled extension entries, which each inline their own copy of this class.
|
|
341
|
+
const WORKFLOW_ERROR_BRAND = Symbol.for("pi-extensible-workflows.workflow-error");
|
|
342
|
+
export class WorkflowError extends Error {
|
|
343
|
+
constructor(public readonly code: WorkflowErrorCode, message: string) { super(message); this.name = "WorkflowError"; (this as unknown as Record<symbol, boolean>)[WORKFLOW_ERROR_BRAND] = true; }
|
|
344
|
+
static [Symbol.hasInstance](value: unknown): boolean {
|
|
345
|
+
if (Function.prototype[Symbol.hasInstance].call(this, value)) return true;
|
|
346
|
+
// Subclasses do not own the brand marker, so they keep plain prototype-chain semantics.
|
|
347
|
+
return Object.hasOwn(this, WORKFLOW_ERROR_BRAND) && typeof value === "object" && value !== null && WORKFLOW_ERROR_BRAND in value;
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
Object.defineProperty(WorkflowError, WORKFLOW_ERROR_BRAND, { value: true });
|
|
351
|
+
export interface WorkflowFailureAgent { id: string; label?: string; role?: string; structuralPath: readonly string[]; attempt: number; transport?: string; session?: WorkflowAgentSessionReference }
|
|
352
|
+
export interface WorkflowSiblingAgent { id: string; label?: string; role?: string; structuralPath: readonly string[] }
|
|
353
|
+
export interface WorkflowFailureDiagnostics { runId: string; workflowName: string; state: RunState; failedAt: string | null; error: WorkflowErrorShape; failedAgent?: WorkflowFailureAgent; completedSiblingAgents?: readonly WorkflowSiblingAgent[]; completedSiblingPaths: readonly (readonly string[])[]; retry?: { sourceRunId: string; action: string; completedPaths: readonly string[]; incompletePaths: readonly string[]; namedWorktrees: readonly string[]; warning: string }; artifacts: { runDirectory: string; statePath: string; journalPath: string } }
|
|
354
|
+
export interface CheckpointInput { name: string; prompt: string; context: JsonValue }
|
|
355
|
+
export interface FunctionIdentity { path: string; structuralPath: readonly string[]; occurrence: number; worktreeOwner?: string }
|
|
356
|
+
export type AgentContinuity = "fresh" | "continued";
|
|
357
|
+
export interface AgentIdentity { structuralPath: readonly string[]; callSite: string; occurrence: number; parentBreadcrumb?: string; worktreeOwner?: string; handle?: string; turn?: number }
|
|
358
|
+
export interface ShellIdentity { structuralPath: readonly string[]; callSite: string; occurrence: number; worktreeOwner?: string }
|
|
359
|
+
export interface WorkflowBridge { agent?: (prompt: string, options: Readonly<Record<string, JsonValue>>, signal: AbortSignal, identity: AgentIdentity) => Promise<JsonValue>; shell?: (command: string, options: ShellOptions, signal: AbortSignal, identity: ShellIdentity) => Promise<ShellResult>; checkpoint?: (input: Readonly<Record<string, JsonValue>>, signal: AbortSignal) => boolean | Promise<boolean>; function?: (name: string, input: Readonly<Record<string, JsonValue>>, signal: AbortSignal, identity: FunctionIdentity) => Promise<JsonValue>; worktree?: (owner: string, signal: AbortSignal) => Promise<Readonly<WorkflowWorktreeReference>>; functions?: Readonly<Record<string, { name: string }>>; phase?: (name: string) => void | Promise<void>; log?: (message: string) => void | Promise<void> }
|
|
360
|
+
export interface WorkflowExecution { result: Promise<JsonValue>; cancel: () => void }
|
|
361
|
+
export interface StaticWorkflowScope { kind: "parallel" | "pipeline"; name: string | null; key: string | null }
|
|
362
|
+
export type StaticWorkflowExecution = "parallel" | "sequential";
|
|
363
|
+
export interface StaticWorkflowCall { kind: WorkflowCallKind; start: number; end: number; name: string | null; prompt: string | null; model: string | null; label?: string | null; role: string | null; retries?: number | null; outputSchema?: JsonSchema | null; options?: Readonly<Record<string, JsonValue>> | null; optionKeys?: readonly string[]; execution?: StaticWorkflowExecution; structure?: readonly StaticWorkflowScope[] }
|
|
364
|
+
export interface WorkflowCatalogFunction { name: string; version: string; headline: string; description: string; input: JsonSchema; output: JsonSchema }
|
|
365
|
+
export interface WorkflowCatalogModelAlias { name: string; kind: "static" | "dynamic"; provenance: string; version?: string; headline?: string }
|
|
366
|
+
export interface WorkflowCatalogSettings { concurrency: number; backgroundWidget: boolean; modelAliases: Readonly<Record<string, string>>; skills: readonly string[]; extensions: readonly string[]; tools: readonly string[]; extensionSettings?: Readonly<WorkflowExtensionSettings>; worktreePostCreateCommand?: readonly string[]; globalSettingsPath: string; projectSettingsPath: string; projectTrusted: boolean; sources: WorkflowSettingsSources }
|
|
367
|
+
export interface WorkflowCatalogContext { cwd: string; projectTrusted: boolean; globalSettingsPath?: string }
|
|
368
|
+
export interface WorkflowCatalog { functions: readonly WorkflowCatalogFunction[]; modelAliases?: Readonly<Record<string, string>>; modelAliasEntries?: readonly WorkflowCatalogModelAlias[]; settings?: WorkflowCatalogSettings }
|
|
369
|
+
export interface WorkflowCatalogIndexFunction { name: string; description: string; input: JsonSchema }
|
|
370
|
+
export interface WorkflowCatalogIndex { functions: readonly WorkflowCatalogIndexFunction[]; modelAliases?: Readonly<Record<string, string>>; modelAliasEntries?: readonly WorkflowCatalogModelAlias[]; settings?: WorkflowCatalogSettings }
|
|
371
|
+
export interface WorkflowCatalogError { error: { code: "NOT_FOUND"; name: string; message: string } }
|
|
372
|
+
export interface WorkflowValidationParameters { name: string; description?: string; script?: string; scriptPath?: string; args?: unknown }
|
|
373
|
+
export interface WorkflowValidationContext { cwd: string; projectTrusted: boolean; availableModels: ReadonlySet<string>; rootTools: ReadonlySet<string>; modelAliases?: Readonly<Record<string, string>>; knownModels?: ReadonlySet<string>; settingsPath?: string; agentDir?: string; extensionSettings?: Readonly<WorkflowExtensionSettings> }
|
|
374
|
+
export interface ValidatedWorkflowLaunch { script: string; checked: PreflightResult; agentDefinitions: Readonly<Record<string, AgentDefinition>>; projectAgentDefinitions: Readonly<Record<string, AgentDefinition>>; roleNames: readonly string[] }
|
package/src/utils.ts
ADDED
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
import { ERROR_CODES, LAUNCH_SNAPSHOT_IDENTITY_VERSION, THINKING_LEVELS, WorkflowError, type JsonValue, type ModelSpec, type ThinkingLevel, type WorkflowErrorCode, type WorkflowExtensionSettings } from "./types.js";
|
|
2
|
+
import { Minimatch } from "minimatch";
|
|
3
|
+
export class SerialLane {
|
|
4
|
+
#tail: Promise<void> = Promise.resolve();
|
|
5
|
+
run<T>(task: () => Promise<T>): Promise<T> {
|
|
6
|
+
const next = this.#tail.then(task, task);
|
|
7
|
+
this.#tail = next.then(() => undefined, () => undefined);
|
|
8
|
+
return next;
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export function object(value: unknown): value is Record<string, unknown> { return typeof value === "object" && value !== null && !Array.isArray(value); }
|
|
13
|
+
const ESCAPE_CHARACTER = String.fromCharCode(27);
|
|
14
|
+
const BELL_CHARACTER = String.fromCharCode(7);
|
|
15
|
+
const ANSI_ESCAPE_SEQUENCE = new RegExp(`${ESCAPE_CHARACTER}(?:\\[[0-?]*[ -/]*[@-~]|\\][^${BELL_CHARACTER}]*(?:${BELL_CHARACTER}|${ESCAPE_CHARACTER}\\\\))`, "g");
|
|
16
|
+
const TERMINAL_CONTROL_CHARACTER = new RegExp(`[${String.fromCharCode(0)}-${String.fromCharCode(31)}${String.fromCharCode(127)}-${String.fromCharCode(159)}]`, "g");
|
|
17
|
+
export function sanitizeDisplayText(value: string): string { return value.replace(ANSI_ESCAPE_SEQUENCE, "").replace(TERMINAL_CONTROL_CHARACTER, " "); }
|
|
18
|
+
export { object as isObject };
|
|
19
|
+
function isStringKey(key: PropertyKey): key is string { return typeof key === "string"; }
|
|
20
|
+
function stringKeyValue(value: object, key: string): unknown { return object(value) ? value[key] : undefined; }
|
|
21
|
+
export function jsonValue(value: unknown, seen = new Set<object>()): value is JsonValue {
|
|
22
|
+
if (value === null || typeof value === "boolean" || typeof value === "string") return true;
|
|
23
|
+
if (typeof value === "number") return Number.isFinite(value);
|
|
24
|
+
if (typeof value !== "object" || seen.has(value)) return false;
|
|
25
|
+
if (!Array.isArray(value) && Object.getPrototypeOf(value) !== Object.prototype && Object.getPrototypeOf(value) !== null) return false;
|
|
26
|
+
const ownKeys = Reflect.ownKeys(value);
|
|
27
|
+
const keys = ownKeys.filter(isStringKey);
|
|
28
|
+
if (keys.length !== ownKeys.length) return false;
|
|
29
|
+
seen.add(value);
|
|
30
|
+
const valid = (Array.isArray(value) ? Array.from(value) : keys.map((key) => stringKeyValue(value, key))).every((item) => jsonValue(item, seen));
|
|
31
|
+
seen.delete(value);
|
|
32
|
+
return valid;
|
|
33
|
+
}
|
|
34
|
+
export function jsonObject(value: unknown): value is Record<string, JsonValue> { return jsonValue(value) && object(value); }
|
|
35
|
+
export function mergeWorkflowExtensionSettings(...layers: readonly (Readonly<WorkflowExtensionSettings> | undefined)[]): Readonly<WorkflowExtensionSettings> | undefined {
|
|
36
|
+
const merged: Record<string, JsonValue> = {};
|
|
37
|
+
let present = false;
|
|
38
|
+
for (const layer of layers) {
|
|
39
|
+
if (layer === undefined) continue;
|
|
40
|
+
present = true;
|
|
41
|
+
for (const [namespace, value] of Object.entries(layer)) Object.defineProperty(merged, namespace, { value: structuredClone(value), enumerable: true, configurable: true, writable: true });
|
|
42
|
+
}
|
|
43
|
+
return present ? deepFreeze(merged as WorkflowExtensionSettings) : undefined;
|
|
44
|
+
}
|
|
45
|
+
export function positiveInteger(value: unknown): value is number { return typeof value === "number" && Number.isInteger(value) && value > 0; }
|
|
46
|
+
export function finiteNumber(value: unknown): value is number { return typeof value === "number" && Number.isFinite(value); }
|
|
47
|
+
export function deepFreeze<T>(value: T): T {
|
|
48
|
+
if (typeof value === "object" && value !== null && !Object.isFrozen(value)) {
|
|
49
|
+
Object.freeze(value);
|
|
50
|
+
for (const child of Object.values(value)) deepFreeze(child);
|
|
51
|
+
}
|
|
52
|
+
return value;
|
|
53
|
+
}
|
|
54
|
+
export function isNodeError(error: unknown, code: string): error is { code: string } { return object(error) && error.code === code; }
|
|
55
|
+
export function isWorkflowErrorCode(value: unknown): value is WorkflowErrorCode { return ERROR_CODES.some((candidate) => candidate === value); }
|
|
56
|
+
export function errorText(error: unknown): string { return object(error) && typeof error.message === "string" ? error.message : error instanceof Error ? error.message : String(error); }
|
|
57
|
+
export function coerceWorkflowError(code: WorkflowErrorCode, error: unknown): WorkflowError {
|
|
58
|
+
return error instanceof WorkflowError && error.code === code ? error : new WorkflowError(code, errorText(error));
|
|
59
|
+
}
|
|
60
|
+
export function errorCode(error: unknown): WorkflowErrorCode | undefined {
|
|
61
|
+
if (error instanceof WorkflowError) return isWorkflowErrorCode(error.code) ? error.code : undefined;
|
|
62
|
+
if (!object(error)) return undefined;
|
|
63
|
+
return isWorkflowErrorCode(error.code) ? error.code : undefined;
|
|
64
|
+
}
|
|
65
|
+
const WORKFLOW_AUTHORED_ERROR = Symbol("workflowAuthoredError");
|
|
66
|
+
export function markWorkflowAuthored(error: WorkflowError, authored = false): WorkflowError {
|
|
67
|
+
if (authored) Object.defineProperty(error, WORKFLOW_AUTHORED_ERROR, { value: true });
|
|
68
|
+
return error;
|
|
69
|
+
}
|
|
70
|
+
export function isWorkflowAuthored(error: unknown): boolean { return Boolean(error && typeof error === "object" && WORKFLOW_AUTHORED_ERROR in error); }
|
|
71
|
+
export function asWorkflowError(error: unknown): WorkflowError {
|
|
72
|
+
const code = errorCode(error);
|
|
73
|
+
return markWorkflowAuthored(error instanceof WorkflowError && code ? error : new WorkflowError(code ?? "INTERNAL_ERROR", errorText(error)), isWorkflowAuthored(error) || !code);
|
|
74
|
+
}
|
|
75
|
+
export function fail(code: WorkflowErrorCode, message: string): never { throw new WorkflowError(code, message); }
|
|
76
|
+
/** Sort order for agent setup hooks: lower priority first, ties broken by name so registration order never matters. */
|
|
77
|
+
export function byPriorityThenName(left: { priority: number; name: string }, right: { priority: number; name: string }): number { return left.priority - right.priority || (left.name < right.name ? -1 : left.name > right.name ? 1 : 0); }
|
|
78
|
+
|
|
79
|
+
export function isThinkingLevel(value: unknown): value is ThinkingLevel { return THINKING_LEVELS.some((level) => level === value); }
|
|
80
|
+
export const MODEL_ALIAS_NAME = /^[A-Za-z][A-Za-z0-9_-]*$/;
|
|
81
|
+
export const WORKFLOW_EXTENSION_NAMESPACE = /^[A-Za-z][A-Za-z0-9_-]*$/;
|
|
82
|
+
export function validWorkflowExtensionNamespace(value: string): boolean { return WORKFLOW_EXTENSION_NAMESPACE.test(value) && value !== "__proto__" && value !== "constructor" && value !== "prototype"; }
|
|
83
|
+
export function parseThinking(value: unknown): ModelSpec["thinking"] | undefined { return isThinkingLevel(value) ? value : undefined; }
|
|
84
|
+
export function parseModelReference(value: string): ModelSpec {
|
|
85
|
+
const match = /^([^/:\s]+)\/([^:\s]+)(?::([^:\s]+))?$/.exec(value);
|
|
86
|
+
if (!match?.[1] || !match[2]) fail("UNKNOWN_MODEL", `Invalid model spec: ${value}`);
|
|
87
|
+
const thinking = match[3];
|
|
88
|
+
if (thinking !== undefined && !isThinkingLevel(thinking)) fail("UNKNOWN_MODEL", `Invalid thinking level: ${thinking}`);
|
|
89
|
+
return { provider: match[1], model: match[2], ...(thinking !== undefined ? { thinking } : {}) };
|
|
90
|
+
}
|
|
91
|
+
export function assertModelThinking(value: string, path = "model"): void {
|
|
92
|
+
if (value.includes("/") && parseModelReference(value).thinking === undefined) fail("INVALID_METADATA", `${path} must be provider/model:thinking`);
|
|
93
|
+
}
|
|
94
|
+
const MODEL_ALIAS_ERROR_NAME = Symbol("modelAliasErrorName");
|
|
95
|
+
type ModelAliasError = WorkflowError & { [MODEL_ALIAS_ERROR_NAME]?: string };
|
|
96
|
+
function aliasError(message: string, settingsPath: string, name?: string): never {
|
|
97
|
+
const error = new WorkflowError("CONFIG_ERROR", `${message} (settings: ${settingsPath})`);
|
|
98
|
+
if (name) Object.defineProperty(error, MODEL_ALIAS_ERROR_NAME, { value: name, configurable: true });
|
|
99
|
+
throw error;
|
|
100
|
+
}
|
|
101
|
+
export function annotateModelAliasError(error: unknown, name: string): unknown {
|
|
102
|
+
if (error instanceof WorkflowError) Object.defineProperty(error, MODEL_ALIAS_ERROR_NAME, { value: name, configurable: true });
|
|
103
|
+
return error;
|
|
104
|
+
}
|
|
105
|
+
export function modelAliasErrorName(error: unknown): string | undefined {
|
|
106
|
+
return error instanceof WorkflowError ? (error as ModelAliasError)[MODEL_ALIAS_ERROR_NAME] : undefined;
|
|
107
|
+
}
|
|
108
|
+
export function modelAliasName(value: string, aliases: Readonly<Record<string, string>>): string | undefined {
|
|
109
|
+
const name = /^([^/:\s]+)(?::[^:\s]+)?$/.exec(value)?.[1];
|
|
110
|
+
return name && Object.prototype.hasOwnProperty.call(aliases, name) ? name : undefined;
|
|
111
|
+
}
|
|
112
|
+
export function validateModelAliases(value: unknown, settingsPath = "workflow settings"): Readonly<Record<string, string>> {
|
|
113
|
+
if (!object(value)) aliasError("modelAliases must be an object", settingsPath);
|
|
114
|
+
const aliases: Record<string, string> = {};
|
|
115
|
+
for (const [name, target] of Object.entries(value)) {
|
|
116
|
+
if (!MODEL_ALIAS_NAME.test(name)) aliasError(`Invalid model alias name: ${name}`, settingsPath, name);
|
|
117
|
+
if (typeof target !== "string" || !target.trim()) aliasError(`Invalid model alias target for ${name}`, settingsPath, name);
|
|
118
|
+
aliases[name] = target;
|
|
119
|
+
}
|
|
120
|
+
for (const name of Object.keys(aliases)) {
|
|
121
|
+
try { resolveModelReference(name, aliases); } catch (error) { aliasError(`Invalid model alias target for ${name}: ${errorText(error)}`, settingsPath, name); }
|
|
122
|
+
}
|
|
123
|
+
return Object.freeze(aliases);
|
|
124
|
+
}
|
|
125
|
+
export function unknownModel(value: string, target: string | undefined, settingsPath?: string): never {
|
|
126
|
+
const resolved = target ? ` resolved to ${target}` : "";
|
|
127
|
+
const path = settingsPath ? ` (settings: ${settingsPath})` : "";
|
|
128
|
+
fail("UNKNOWN_MODEL", `Unknown model${target ? " alias" : ""} ${value}${resolved}${path}`);
|
|
129
|
+
}
|
|
130
|
+
export function resolveModelReference(value: string, aliases: Readonly<Record<string, string>> = {}, knownModels?: ReadonlySet<string>, settingsPath?: string): ModelSpec {
|
|
131
|
+
const resolveReference = (reference: string, chain: readonly string[]): ModelSpec => {
|
|
132
|
+
if (reference.includes("/")) return parseModelReference(reference);
|
|
133
|
+
const match = /^([^:\s]+)(?::([^:\s]+))?$/.exec(reference);
|
|
134
|
+
const thinking = match?.[2];
|
|
135
|
+
if (!match?.[1] || thinking !== undefined && !isThinkingLevel(thinking)) unknownModel(reference, undefined, settingsPath);
|
|
136
|
+
const alias = modelAliasName(reference, aliases);
|
|
137
|
+
if (alias) {
|
|
138
|
+
if (chain.includes(alias)) fail("UNKNOWN_MODEL", `Circular model alias: ${[...chain, alias].join(" -> ")}${settingsPath ? ` (settings: ${settingsPath})` : ""}`);
|
|
139
|
+
const { [alias]: target } = aliases;
|
|
140
|
+
if (typeof target !== "string") unknownModel(reference, undefined, settingsPath);
|
|
141
|
+
const parsed = resolveReference(target, [...chain, alias]);
|
|
142
|
+
return thinking !== undefined ? { ...parsed, thinking } : parsed;
|
|
143
|
+
}
|
|
144
|
+
const candidates = [...(knownModels ?? [])].filter((model) => model.slice(model.indexOf("/") + 1) === match[1]);
|
|
145
|
+
if (candidates.length === 1) {
|
|
146
|
+
const [candidate] = candidates;
|
|
147
|
+
if (candidate !== undefined) {
|
|
148
|
+
const parsed = parseModelReference(candidate);
|
|
149
|
+
return thinking !== undefined ? { ...parsed, thinking } : parsed;
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
unknownModel(reference, undefined, settingsPath);
|
|
153
|
+
};
|
|
154
|
+
return resolveReference(value, []);
|
|
155
|
+
}
|
|
156
|
+
export function modelCapability(value: string | ModelSpec, aliases?: Readonly<Record<string, string>>, knownModels?: ReadonlySet<string>, settingsPath?: string): string {
|
|
157
|
+
const parsed = typeof value === "string" ? resolveModelReference(value, aliases, knownModels, settingsPath) : value;
|
|
158
|
+
return `${parsed.provider}/${parsed.model}`;
|
|
159
|
+
}
|
|
160
|
+
export function aliasDrift(previous: Readonly<Record<string, string>>, current: Readonly<Record<string, string>>): string[] {
|
|
161
|
+
return [...new Set([...Object.keys(previous), ...Object.keys(current)])].sort().flatMap((name) => previous[name] === current[name] ? [] : [`${name}: ${previous[name] ?? "(missing)"} -> ${current[name] ?? "(missing)"}`]);
|
|
162
|
+
}
|
|
163
|
+
const RESOURCE_PATTERN_OPTIONS = { dot: true, nonegate: true, nocomment: true } as const;
|
|
164
|
+
function resourcePatternBody(pattern: string): string { return pattern.startsWith("!") ? pattern.slice(1) : pattern; }
|
|
165
|
+
function resourcePatternPath(value: string): string { return value.replaceAll("\\", "/"); }
|
|
166
|
+
export function validateResourcePattern(pattern: string): void {
|
|
167
|
+
const body = resourcePatternBody(pattern);
|
|
168
|
+
if (!body) throw new Error(`Empty minimatch pattern ${JSON.stringify(pattern)}`);
|
|
169
|
+
const matcher = new Minimatch(resourcePatternPath(body), RESOURCE_PATTERN_OPTIONS);
|
|
170
|
+
if (matcher.makeRe() === false) throw new Error(`Invalid minimatch pattern ${JSON.stringify(pattern)}`);
|
|
171
|
+
}
|
|
172
|
+
export function resourcePatternMatches(resource: string, pattern: string): boolean {
|
|
173
|
+
const body = resourcePatternBody(pattern);
|
|
174
|
+
if (body === "*") return true;
|
|
175
|
+
return new Minimatch(resourcePatternPath(body), RESOURCE_PATTERN_OPTIONS).match(resourcePatternPath(resource));
|
|
176
|
+
}
|
|
177
|
+
export function selectResourcesByLayers(layers: readonly (readonly string[] | undefined)[], resources: readonly string[]): string[] {
|
|
178
|
+
const enabled = new Set(resources);
|
|
179
|
+
for (const layer of layers) {
|
|
180
|
+
if (layer === undefined) continue;
|
|
181
|
+
for (const resource of resources) {
|
|
182
|
+
for (const pattern of layer) if (resourcePatternMatches(resource, pattern)) {
|
|
183
|
+
if (pattern.startsWith("!")) enabled.delete(resource); else enabled.add(resource);
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
return resources.filter((resource) => enabled.has(resource));
|
|
188
|
+
}
|
|
189
|
+
export function resourcePatternHasMagic(pattern: string): boolean { return /[*?\x5b\x5d{}()]/.test(resourcePatternBody(pattern)); }
|
|
190
|
+
export function unmatchedResourcePatterns(patterns: readonly string[], resources: readonly string[]): string[] { return patterns.filter((pattern) => !resources.some((resource) => resourcePatternMatches(resource, pattern))); }
|
|
191
|
+
export function createLaunchSnapshot(input: Omit<import("./types.js").LaunchSnapshot, "identityVersion"> & { identityVersion?: number }): Readonly<import("./types.js").LaunchSnapshot> { return deepFreeze(structuredClone({ ...input, identityVersion: input.identityVersion ?? LAUNCH_SNAPSHOT_IDENTITY_VERSION })); }
|
|
192
|
+
export function loadLaunchSnapshot(input: import("./types.js").LaunchSnapshot): Readonly<import("./types.js").LaunchSnapshot> { return deepFreeze(structuredClone(input)); }
|