@yycholla/pi-dynamic-workflows 3.3.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 (179) hide show
  1. package/LICENSE +22 -0
  2. package/README.md +325 -0
  3. package/assets/readme/hero.png +0 -0
  4. package/assets/readme/hero.svg +70 -0
  5. package/assets/readme/package-cover.png +0 -0
  6. package/assets/readme/package-cover.svg +66 -0
  7. package/assets/readme/workflow.png +0 -0
  8. package/assets/readme/workflow.svg +77 -0
  9. package/dist/accept-workflow-guidance.d.ts +14 -0
  10. package/dist/accept-workflow-guidance.js +53 -0
  11. package/dist/adversarial-review.d.ts +30 -0
  12. package/dist/adversarial-review.js +107 -0
  13. package/dist/agent-history.d.ts +20 -0
  14. package/dist/agent-history.js +122 -0
  15. package/dist/agent-registry.d.ts +83 -0
  16. package/dist/agent-registry.js +190 -0
  17. package/dist/agent.d.ts +364 -0
  18. package/dist/agent.js +714 -0
  19. package/dist/builtin-commands.d.ts +19 -0
  20. package/dist/builtin-commands.js +251 -0
  21. package/dist/builtin-workflows.d.ts +45 -0
  22. package/dist/builtin-workflows.js +121 -0
  23. package/dist/code-review.d.ts +26 -0
  24. package/dist/code-review.js +181 -0
  25. package/dist/config.d.ts +37 -0
  26. package/dist/config.js +44 -0
  27. package/dist/deep-research.d.ts +30 -0
  28. package/dist/deep-research.js +124 -0
  29. package/dist/display.d.ts +134 -0
  30. package/dist/display.js +248 -0
  31. package/dist/effort-command.d.ts +28 -0
  32. package/dist/effort-command.js +68 -0
  33. package/dist/enums.d.ts +69 -0
  34. package/dist/enums.js +78 -0
  35. package/dist/errors.d.ts +113 -0
  36. package/dist/errors.js +140 -0
  37. package/dist/extension-reload.d.ts +37 -0
  38. package/dist/extension-reload.js +78 -0
  39. package/dist/fs-persistence.d.ts +63 -0
  40. package/dist/fs-persistence.js +102 -0
  41. package/dist/index.d.ts +57 -0
  42. package/dist/index.js +35 -0
  43. package/dist/logger.d.ts +21 -0
  44. package/dist/logger.js +66 -0
  45. package/dist/model-routing.d.ts +30 -0
  46. package/dist/model-routing.js +50 -0
  47. package/dist/model-spec.d.ts +29 -0
  48. package/dist/model-spec.js +252 -0
  49. package/dist/model-tier-config.d.ts +133 -0
  50. package/dist/model-tier-config.js +249 -0
  51. package/dist/run-persistence.d.ts +180 -0
  52. package/dist/run-persistence.js +294 -0
  53. package/dist/saved-commands.d.ts +28 -0
  54. package/dist/saved-commands.js +100 -0
  55. package/dist/shared-store.d.ts +98 -0
  56. package/dist/shared-store.js +212 -0
  57. package/dist/structured-output.d.ts +19 -0
  58. package/dist/structured-output.js +30 -0
  59. package/dist/task-panel.d.ts +61 -0
  60. package/dist/task-panel.js +422 -0
  61. package/dist/usage-limit-scheduler.d.ts +145 -0
  62. package/dist/usage-limit-scheduler.js +368 -0
  63. package/dist/web-tools.d.ts +20 -0
  64. package/dist/web-tools.js +120 -0
  65. package/dist/workflow-authoring-coverage.d.ts +70 -0
  66. package/dist/workflow-authoring-coverage.js +421 -0
  67. package/dist/workflow-authoring-reference.d.ts +20 -0
  68. package/dist/workflow-authoring-reference.js +156 -0
  69. package/dist/workflow-capability-contract.d.ts +131 -0
  70. package/dist/workflow-capability-contract.js +604 -0
  71. package/dist/workflow-commands.d.ts +18 -0
  72. package/dist/workflow-commands.js +260 -0
  73. package/dist/workflow-comprehension.d.ts +133 -0
  74. package/dist/workflow-comprehension.js +1321 -0
  75. package/dist/workflow-context-measurement.d.ts +72 -0
  76. package/dist/workflow-context-measurement.js +213 -0
  77. package/dist/workflow-control-tool.d.ts +30 -0
  78. package/dist/workflow-control-tool.js +176 -0
  79. package/dist/workflow-delivery-choice.d.ts +20 -0
  80. package/dist/workflow-delivery-choice.js +48 -0
  81. package/dist/workflow-editor.d.ts +93 -0
  82. package/dist/workflow-editor.js +363 -0
  83. package/dist/workflow-manager.d.ts +492 -0
  84. package/dist/workflow-manager.js +1124 -0
  85. package/dist/workflow-paths.d.ts +22 -0
  86. package/dist/workflow-paths.js +46 -0
  87. package/dist/workflow-release-gate.d.ts +39 -0
  88. package/dist/workflow-release-gate.js +309 -0
  89. package/dist/workflow-saved.d.ts +38 -0
  90. package/dist/workflow-saved.js +126 -0
  91. package/dist/workflow-settings.d.ts +70 -0
  92. package/dist/workflow-settings.js +131 -0
  93. package/dist/workflow-tool.d.ts +71 -0
  94. package/dist/workflow-tool.js +367 -0
  95. package/dist/workflow-ui.d.ts +182 -0
  96. package/dist/workflow-ui.js +1587 -0
  97. package/dist/workflow.d.ts +333 -0
  98. package/dist/workflow.js +1151 -0
  99. package/dist/workflows-models-command.d.ts +31 -0
  100. package/dist/workflows-models-command.js +156 -0
  101. package/dist/worktree.d.ts +25 -0
  102. package/dist/worktree.js +61 -0
  103. package/extensions/workflow.ts +151 -0
  104. package/package.json +104 -0
  105. package/skills/workflow-authoring/SKILL.md +30 -0
  106. package/skills/workflow-authoring/examples/adversarial-verification.js +63 -0
  107. package/skills/workflow-authoring/examples/bounded-semantic-retry.js +56 -0
  108. package/skills/workflow-authoring/examples/classify-and-act.js +67 -0
  109. package/skills/workflow-authoring/examples/fan-out-and-synthesize.js +47 -0
  110. package/skills/workflow-authoring/examples/generate-and-filter.js +68 -0
  111. package/skills/workflow-authoring/examples/loop-until-done.js +68 -0
  112. package/skills/workflow-authoring/examples/phased-budgets.js +75 -0
  113. package/skills/workflow-authoring/examples/saved-nested-workflows.js +44 -0
  114. package/skills/workflow-authoring/examples/structured-output.js +37 -0
  115. package/skills/workflow-authoring/examples/tournament.js +81 -0
  116. package/skills/workflow-authoring/examples/validated-gate.js +63 -0
  117. package/skills/workflow-authoring/references/capabilities.md +41 -0
  118. package/skills/workflow-authoring/references/capability-details.md +357 -0
  119. package/skills/workflow-authoring/references/common-helpers.md +4 -0
  120. package/skills/workflow-authoring/references/debugging.md +27 -0
  121. package/skills/workflow-authoring/references/focused-recipes.md +11 -0
  122. package/skills/workflow-authoring/references/helpers.md +9 -0
  123. package/skills/workflow-authoring/references/lifecycle.md +33 -0
  124. package/skills/workflow-authoring/references/pattern-selection.md +15 -0
  125. package/skills/workflow-authoring/references/quality-helpers.md +8 -0
  126. package/skills/workflow-authoring/references/registry-ownership.md +15 -0
  127. package/skills/workflow-authoring/references/retry-helper.md +5 -0
  128. package/skills/workflow-authoring/references/review.md +43 -0
  129. package/skills/workflow-authoring/references/runtime.md +27 -0
  130. package/skills/workflow-authoring/references/specialized-helpers.md +19 -0
  131. package/skills/workflow-authoring/references/versions.md +13 -0
  132. package/skills/workflow-patterns/SKILL.md +51 -0
  133. package/src/accept-workflow-guidance.ts +71 -0
  134. package/src/adversarial-review.ts +120 -0
  135. package/src/agent-history.ts +157 -0
  136. package/src/agent-registry.ts +221 -0
  137. package/src/agent.ts +929 -0
  138. package/src/builtin-commands.ts +286 -0
  139. package/src/builtin-workflows.ts +155 -0
  140. package/src/code-review.ts +183 -0
  141. package/src/config.ts +55 -0
  142. package/src/deep-research.ts +135 -0
  143. package/src/display.ts +367 -0
  144. package/src/effort-command.ts +87 -0
  145. package/src/enums.ts +77 -0
  146. package/src/errors.ts +199 -0
  147. package/src/extension-reload.ts +100 -0
  148. package/src/fs-persistence.ts +124 -0
  149. package/src/index.ts +176 -0
  150. package/src/logger.ts +88 -0
  151. package/src/model-routing.ts +73 -0
  152. package/src/model-spec.ts +309 -0
  153. package/src/model-tier-config.ts +296 -0
  154. package/src/run-persistence.ts +484 -0
  155. package/src/saved-commands.ts +115 -0
  156. package/src/shared-store.ts +228 -0
  157. package/src/structured-output.ts +47 -0
  158. package/src/task-panel.ts +490 -0
  159. package/src/usage-limit-scheduler.ts +432 -0
  160. package/src/web-tools.ts +124 -0
  161. package/src/workflow-authoring-coverage.ts +486 -0
  162. package/src/workflow-authoring-reference.ts +186 -0
  163. package/src/workflow-capability-contract.ts +806 -0
  164. package/src/workflow-commands.ts +287 -0
  165. package/src/workflow-comprehension.ts +1673 -0
  166. package/src/workflow-context-measurement.ts +262 -0
  167. package/src/workflow-control-tool.ts +238 -0
  168. package/src/workflow-delivery-choice.ts +69 -0
  169. package/src/workflow-editor.ts +444 -0
  170. package/src/workflow-manager.ts +1405 -0
  171. package/src/workflow-paths.ts +63 -0
  172. package/src/workflow-release-gate.ts +529 -0
  173. package/src/workflow-saved.ts +180 -0
  174. package/src/workflow-settings.ts +194 -0
  175. package/src/workflow-tool.ts +464 -0
  176. package/src/workflow-ui.ts +1789 -0
  177. package/src/workflow.ts +1615 -0
  178. package/src/workflows-models-command.ts +211 -0
  179. package/src/worktree.ts +76 -0
@@ -0,0 +1,68 @@
1
+ /**
2
+ * Standing `/effort` opt-in (pi's answer to CC's ultracode): a session toggle that
3
+ * auto-arms a workflow for substantive interactive messages, with effort-tier
4
+ * guidance nudging fan-out breadth and the hard caps (tokenBudget / maxAgents) the
5
+ * model should set on the workflow tool call.
6
+ *
7
+ * Honest scope: the runtime cannot enforce "reviewer N / loop K" — those live in
8
+ * the script the model writes — so the tiers are guidance plus the model setting
9
+ * the real hard caps (tokenBudget/maxAgents are genuine runtime ceilings). The
10
+ * pre-flight ceiling-confirm dialog (roadmap P1-5 #4) is a downscope point: an
11
+ * `input` hook transforms synchronously and can't await a confirm, so it is left
12
+ * to a follow-up; `/effort` is explicit opt-in, which is the safety valve.
13
+ */
14
+ export function createEffortState() {
15
+ return { level: "off" };
16
+ }
17
+ const HIGH_DIRECTIVE = "Effort: HIGH. Be thorough — use a few parallel reviewers/perspectives and an adversarial verify pass (see verify()/judgePanel()); set a moderate tokenBudget and maxAgents on the workflow tool call.";
18
+ const ULTRA_DIRECTIVE = "Effort: ULTRA. Be exhaustive — fan out widely (more reviewers/judges, deeper loopUntilDry rounds, a completenessCheck at the end), and prefer the big tier for synthesis. This can spend a lot of tokens quickly, so set explicit caps you're comfortable paying for (a generous but bounded tokenBudget and a high maxAgents) on the workflow tool call rather than leaving them unbounded.";
19
+ /** The extra directive appended to the forced-workflow prompt for an effort level. */
20
+ export function effortDirective(level) {
21
+ if (level === "high")
22
+ return HIGH_DIRECTIVE;
23
+ if (level === "ultra")
24
+ return ULTRA_DIRECTIVE;
25
+ return undefined;
26
+ }
27
+ /**
28
+ * Whether a message should auto-arm under effort mode: a real interactive request,
29
+ * not a terse acknowledgement or a slash command. (hasTrigger handles the explicit
30
+ * "workflow(s)" keyword separately.)
31
+ */
32
+ export function isSubstantive(text) {
33
+ const t = text.trim();
34
+ return t.length >= 16 && !t.startsWith("/");
35
+ }
36
+ export function registerEffortCommand(pi, state) {
37
+ pi.registerCommand("effort", {
38
+ description: "Standing workflow effort: off | high | ultra — auto-arms a workflow for substantive messages",
39
+ async handler(args, _ctx) {
40
+ const arg = args.trim().toLowerCase();
41
+ const say = (content) => pi.sendMessage({ customType: "effort", content, display: true });
42
+ if (arg === "off" || arg === "high" || arg === "ultra") {
43
+ state.level = arg;
44
+ await say(arg === "off"
45
+ ? "Effort off — messages are no longer auto-armed as workflows."
46
+ : `Effort ${arg} — substantive messages now auto-arm a workflow (${arg === "ultra" ? "exhaustive" : "thorough"} fan-out). Use /effort off to stop.`);
47
+ return;
48
+ }
49
+ await say(`Effort is currently "${state.level}". Usage: /effort off | high | ultra`);
50
+ },
51
+ });
52
+ // `/ultracode` — the headline name for the maximal-effort mode (Pi's ultracode):
53
+ // `/ultracode` turns it on, `/ultracode off` turns it off. Alias for /effort ultra.
54
+ pi.registerCommand("ultracode", {
55
+ description: "Ultracode: standing maximal-effort mode (this session only, never persisted) — auto-arms an exhaustive workflow for substantive messages. /ultracode off to stop.",
56
+ async handler(args, _ctx) {
57
+ const arg = args.trim().toLowerCase();
58
+ const say = (content) => pi.sendMessage({ customType: "effort", content, display: true });
59
+ if (arg === "off") {
60
+ state.level = "off";
61
+ await say("Ultracode off — messages are no longer auto-armed as workflows.");
62
+ return;
63
+ }
64
+ state.level = "ultra";
65
+ await say("Ultracode ON — substantive messages now auto-arm an exhaustive workflow (wide fan-out, big-tier synthesis). Use /ultracode off to stop.");
66
+ },
67
+ });
68
+ }
@@ -0,0 +1,69 @@
1
+ /** Classifies a workflow capability by its runtime and documentation role. */
2
+ export declare enum CapabilityClassification {
3
+ RUNTIME_GLOBAL = "runtime-global",
4
+ WORKFLOW_TOOL_INPUT = "workflow-tool-input",
5
+ SCRIPT_CONTRACT = "script-contract",
6
+ COMPATIBILITY_BEHAVIOR = "compatibility-behavior",
7
+ INTERNAL_SUBSTRATE = "internal-substrate",
8
+ DYNAMIC_REFERENCE = "dynamic-reference"
9
+ }
10
+ /** Declares whether workflow authors should use a capability. */
11
+ export declare enum CapabilitySupport {
12
+ SUPPORTED = "supported",
13
+ COMPATIBILITY = "compatibility",
14
+ INTERNAL = "internal"
15
+ }
16
+ /** Identifies the model-visible surface responsible for discovery. */
17
+ export declare enum DiscoveryPlacement {
18
+ COMPACT_GUIDANCE = "compact-guidance",
19
+ WORKFLOW_AUTHORING_SKILL = "workflow-authoring-skill",
20
+ NONE = "none"
21
+ }
22
+ /** Names the subsystem that owns a capability's behavior. */
23
+ export declare enum CapabilityOrigin {
24
+ PROJECT = "project",
25
+ TOOL_ADAPTER = "tool-adapter",
26
+ VM_REALM = "vm-realm",
27
+ LIVE_CONFIGURATION = "live-configuration"
28
+ }
29
+ /** Severity carried by capability-alignment diagnostics. */
30
+ export declare enum DiagnosticSeverity {
31
+ ERROR = "error",
32
+ WARNING = "warning",
33
+ INFORMATION = "information"
34
+ }
35
+ /** Optional model-comprehension scenario groups. */
36
+ export declare enum ComprehensionSuite {
37
+ QUICK = "quick",
38
+ FULL = "full",
39
+ COVERAGE = "coverage"
40
+ }
41
+ /** Authoring operation exercised by a comprehension scenario. */
42
+ export declare enum ComprehensionTaskKind {
43
+ WRITE = "write",
44
+ EDIT = "edit",
45
+ REVIEW = "review",
46
+ DEBUG = "debug"
47
+ }
48
+ /** Whether authoring guidance may be optimized against behavioral evidence or must remain frozen. */
49
+ export declare enum WorkflowAuthoringProtection {
50
+ BEHAVIORALLY_COVERED = "behaviorally-covered",
51
+ GUIDANCE_FROZEN = "guidance-frozen"
52
+ }
53
+ /** Machine-readable release-gate failure and warning domains. */
54
+ export declare enum WorkflowReleaseDiagnosticCode {
55
+ INCOMPATIBLE_VERSION = "INCOMPATIBLE_VERSION",
56
+ MISSING_BEHAVIOR_EVIDENCE = "MISSING_BEHAVIOR_EVIDENCE",
57
+ UNRESOLVED_BEHAVIOR_EVIDENCE = "UNRESOLVED_BEHAVIOR_EVIDENCE",
58
+ BROKEN_CONTRACT_REFERENCE = "BROKEN_CONTRACT_REFERENCE",
59
+ MISSING_PACKAGE_RESOURCE = "MISSING_PACKAGE_RESOURCE",
60
+ BROKEN_PACKAGE_LINK = "BROKEN_PACKAGE_LINK",
61
+ STALE_GENERATED_SURFACE = "STALE_GENERATED_SURFACE",
62
+ TOOL_INPUT_MISMATCH = "TOOL_INPUT_MISMATCH",
63
+ RUNTIME_CONSTRAINT_DISAGREEMENT = "RUNTIME_CONSTRAINT_DISAGREEMENT",
64
+ NON_CONTRACTUAL_PROSE_DRIFT = "NON_CONTRACTUAL_PROSE_DRIFT",
65
+ MISSING_AUTHORING_COVERAGE = "MISSING_AUTHORING_COVERAGE",
66
+ UNPROTECTED_AUTHORING_GUIDANCE = "UNPROTECTED_AUTHORING_GUIDANCE",
67
+ PROTECTED_GUIDANCE_DRIFT = "PROTECTED_GUIDANCE_DRIFT",
68
+ UNKNOWN_COMPREHENSION_SCENARIO = "UNKNOWN_COMPREHENSION_SCENARIO"
69
+ }
package/dist/enums.js ADDED
@@ -0,0 +1,78 @@
1
+ /** Classifies a workflow capability by its runtime and documentation role. */
2
+ export var CapabilityClassification;
3
+ (function (CapabilityClassification) {
4
+ CapabilityClassification["RUNTIME_GLOBAL"] = "runtime-global";
5
+ CapabilityClassification["WORKFLOW_TOOL_INPUT"] = "workflow-tool-input";
6
+ CapabilityClassification["SCRIPT_CONTRACT"] = "script-contract";
7
+ CapabilityClassification["COMPATIBILITY_BEHAVIOR"] = "compatibility-behavior";
8
+ CapabilityClassification["INTERNAL_SUBSTRATE"] = "internal-substrate";
9
+ CapabilityClassification["DYNAMIC_REFERENCE"] = "dynamic-reference";
10
+ })(CapabilityClassification || (CapabilityClassification = {}));
11
+ /** Declares whether workflow authors should use a capability. */
12
+ export var CapabilitySupport;
13
+ (function (CapabilitySupport) {
14
+ CapabilitySupport["SUPPORTED"] = "supported";
15
+ CapabilitySupport["COMPATIBILITY"] = "compatibility";
16
+ CapabilitySupport["INTERNAL"] = "internal";
17
+ })(CapabilitySupport || (CapabilitySupport = {}));
18
+ /** Identifies the model-visible surface responsible for discovery. */
19
+ export var DiscoveryPlacement;
20
+ (function (DiscoveryPlacement) {
21
+ DiscoveryPlacement["COMPACT_GUIDANCE"] = "compact-guidance";
22
+ DiscoveryPlacement["WORKFLOW_AUTHORING_SKILL"] = "workflow-authoring-skill";
23
+ DiscoveryPlacement["NONE"] = "none";
24
+ })(DiscoveryPlacement || (DiscoveryPlacement = {}));
25
+ /** Names the subsystem that owns a capability's behavior. */
26
+ export var CapabilityOrigin;
27
+ (function (CapabilityOrigin) {
28
+ CapabilityOrigin["PROJECT"] = "project";
29
+ CapabilityOrigin["TOOL_ADAPTER"] = "tool-adapter";
30
+ CapabilityOrigin["VM_REALM"] = "vm-realm";
31
+ CapabilityOrigin["LIVE_CONFIGURATION"] = "live-configuration";
32
+ })(CapabilityOrigin || (CapabilityOrigin = {}));
33
+ /** Severity carried by capability-alignment diagnostics. */
34
+ export var DiagnosticSeverity;
35
+ (function (DiagnosticSeverity) {
36
+ DiagnosticSeverity["ERROR"] = "error";
37
+ DiagnosticSeverity["WARNING"] = "warning";
38
+ DiagnosticSeverity["INFORMATION"] = "information";
39
+ })(DiagnosticSeverity || (DiagnosticSeverity = {}));
40
+ /** Optional model-comprehension scenario groups. */
41
+ export var ComprehensionSuite;
42
+ (function (ComprehensionSuite) {
43
+ ComprehensionSuite["QUICK"] = "quick";
44
+ ComprehensionSuite["FULL"] = "full";
45
+ ComprehensionSuite["COVERAGE"] = "coverage";
46
+ })(ComprehensionSuite || (ComprehensionSuite = {}));
47
+ /** Authoring operation exercised by a comprehension scenario. */
48
+ export var ComprehensionTaskKind;
49
+ (function (ComprehensionTaskKind) {
50
+ ComprehensionTaskKind["WRITE"] = "write";
51
+ ComprehensionTaskKind["EDIT"] = "edit";
52
+ ComprehensionTaskKind["REVIEW"] = "review";
53
+ ComprehensionTaskKind["DEBUG"] = "debug";
54
+ })(ComprehensionTaskKind || (ComprehensionTaskKind = {}));
55
+ /** Whether authoring guidance may be optimized against behavioral evidence or must remain frozen. */
56
+ export var WorkflowAuthoringProtection;
57
+ (function (WorkflowAuthoringProtection) {
58
+ WorkflowAuthoringProtection["BEHAVIORALLY_COVERED"] = "behaviorally-covered";
59
+ WorkflowAuthoringProtection["GUIDANCE_FROZEN"] = "guidance-frozen";
60
+ })(WorkflowAuthoringProtection || (WorkflowAuthoringProtection = {}));
61
+ /** Machine-readable release-gate failure and warning domains. */
62
+ export var WorkflowReleaseDiagnosticCode;
63
+ (function (WorkflowReleaseDiagnosticCode) {
64
+ WorkflowReleaseDiagnosticCode["INCOMPATIBLE_VERSION"] = "INCOMPATIBLE_VERSION";
65
+ WorkflowReleaseDiagnosticCode["MISSING_BEHAVIOR_EVIDENCE"] = "MISSING_BEHAVIOR_EVIDENCE";
66
+ WorkflowReleaseDiagnosticCode["UNRESOLVED_BEHAVIOR_EVIDENCE"] = "UNRESOLVED_BEHAVIOR_EVIDENCE";
67
+ WorkflowReleaseDiagnosticCode["BROKEN_CONTRACT_REFERENCE"] = "BROKEN_CONTRACT_REFERENCE";
68
+ WorkflowReleaseDiagnosticCode["MISSING_PACKAGE_RESOURCE"] = "MISSING_PACKAGE_RESOURCE";
69
+ WorkflowReleaseDiagnosticCode["BROKEN_PACKAGE_LINK"] = "BROKEN_PACKAGE_LINK";
70
+ WorkflowReleaseDiagnosticCode["STALE_GENERATED_SURFACE"] = "STALE_GENERATED_SURFACE";
71
+ WorkflowReleaseDiagnosticCode["TOOL_INPUT_MISMATCH"] = "TOOL_INPUT_MISMATCH";
72
+ WorkflowReleaseDiagnosticCode["RUNTIME_CONSTRAINT_DISAGREEMENT"] = "RUNTIME_CONSTRAINT_DISAGREEMENT";
73
+ WorkflowReleaseDiagnosticCode["NON_CONTRACTUAL_PROSE_DRIFT"] = "NON_CONTRACTUAL_PROSE_DRIFT";
74
+ WorkflowReleaseDiagnosticCode["MISSING_AUTHORING_COVERAGE"] = "MISSING_AUTHORING_COVERAGE";
75
+ WorkflowReleaseDiagnosticCode["UNPROTECTED_AUTHORING_GUIDANCE"] = "UNPROTECTED_AUTHORING_GUIDANCE";
76
+ WorkflowReleaseDiagnosticCode["PROTECTED_GUIDANCE_DRIFT"] = "PROTECTED_GUIDANCE_DRIFT";
77
+ WorkflowReleaseDiagnosticCode["UNKNOWN_COMPREHENSION_SCENARIO"] = "UNKNOWN_COMPREHENSION_SCENARIO";
78
+ })(WorkflowReleaseDiagnosticCode || (WorkflowReleaseDiagnosticCode = {}));
@@ -0,0 +1,113 @@
1
+ /**
2
+ * Workflow-specific error types.
3
+ */
4
+ /** Dependency-neutral diagnostic payload retained by capability contract failures. */
5
+ export interface CapabilityErrorDiagnostic {
6
+ code: string;
7
+ severity: "error" | "warning" | "information";
8
+ subject: string;
9
+ message: string;
10
+ }
11
+ /** Dependency-neutral skill-loading payload retained by generation failures. */
12
+ export interface ModelGenerationSkillLoadingEvidence {
13
+ discovered: boolean;
14
+ loaded: boolean;
15
+ toolCalls: Array<{
16
+ tool: string;
17
+ path?: string;
18
+ }>;
19
+ }
20
+ /** Dependency-neutral provider-usage payload retained by generation failures. */
21
+ export interface ModelGenerationTokenUsage {
22
+ input: number;
23
+ output: number;
24
+ total: number;
25
+ cost: number;
26
+ cacheRead: number;
27
+ cacheWrite: number;
28
+ }
29
+ /** Stable runtime and persistence failure codes exposed to callers and UI surfaces. */
30
+ export declare enum WorkflowErrorCode {
31
+ /** Agent exceeded timeout. */
32
+ AGENT_TIMEOUT = "AGENT_TIMEOUT",
33
+ /** Workflow was aborted by user. */
34
+ WORKFLOW_ABORTED = "WORKFLOW_ABORTED",
35
+ /** Agent limit exceeded. */
36
+ AGENT_LIMIT_EXCEEDED = "AGENT_LIMIT_EXCEEDED",
37
+ /** Token budget exhausted. */
38
+ TOKEN_BUDGET_EXHAUSTED = "TOKEN_BUDGET_EXHAUSTED",
39
+ /**
40
+ * The provider's subscription/usage/quota/rate limit was hit. Distinct from the
41
+ * user's self-imposed TOKEN_BUDGET_EXHAUSTED: a provider limit refills on its own,
42
+ * so the run is checkpointed (paused) and replayed by resume() rather than failed.
43
+ */
44
+ PROVIDER_USAGE_LIMIT = "PROVIDER_USAGE_LIMIT",
45
+ /** Script validation failed. */
46
+ SCRIPT_VALIDATION_ERROR = "SCRIPT_VALIDATION_ERROR",
47
+ /** A schema agent never produced valid structured_output (after repair + extraction). */
48
+ SCHEMA_NONCOMPLIANCE = "SCHEMA_NONCOMPLIANCE",
49
+ /** A non-schema agent completed without any assistant text output. */
50
+ AGENT_EMPTY_OUTPUT = "AGENT_EMPTY_OUTPUT",
51
+ /** Agent execution failed. */
52
+ AGENT_EXECUTION_ERROR = "AGENT_EXECUTION_ERROR",
53
+ /** Run state persistence failed. */
54
+ PERSISTENCE_ERROR = "PERSISTENCE_ERROR",
55
+ /** Unknown error. */
56
+ UNKNOWN = "UNKNOWN"
57
+ }
58
+ /** Classified workflow failure with recoverability and optional agent/provider context. */
59
+ export declare class WorkflowError extends Error {
60
+ readonly code: WorkflowErrorCode;
61
+ readonly recoverable: boolean;
62
+ readonly agentLabel?: string;
63
+ readonly details?: unknown;
64
+ /** For PROVIDER_USAGE_LIMIT: the provider's human reset hint, e.g. "Resets in ~3h" (verbatim). */
65
+ readonly resetHint?: string;
66
+ constructor(message: string, code: WorkflowErrorCode, options?: {
67
+ recoverable?: boolean;
68
+ agentLabel?: string;
69
+ details?: unknown;
70
+ resetHint?: string;
71
+ });
72
+ }
73
+ /** Contract failure that retains every definition or assembly diagnostic. */
74
+ export declare class WorkflowCapabilityContractError extends Error {
75
+ readonly diagnostics: readonly CapabilityErrorDiagnostic[];
76
+ constructor(message: string, diagnostics: readonly CapabilityErrorDiagnostic[]);
77
+ }
78
+ /** Generation failure that retains loading and token evidence for diagnosis. */
79
+ export declare class ModelGenerationError extends Error {
80
+ readonly skillLoadingEvidence: ModelGenerationSkillLoadingEvidence;
81
+ readonly tokenUsage: ModelGenerationTokenUsage;
82
+ constructor(message: string, skillLoadingEvidence: ModelGenerationSkillLoadingEvidence, tokenUsage: ModelGenerationTokenUsage);
83
+ }
84
+ /** Narrow an unknown failure to WorkflowError. */
85
+ export declare function isWorkflowError(error: unknown): error is WorkflowError;
86
+ /** Report whether an unknown failure is a provider usage-limit checkpoint condition. */
87
+ export declare function isProviderUsageLimit(error: unknown): error is WorkflowError;
88
+ /**
89
+ * Detect a provider subscription/usage/quota/rate-limit exhaustion from free-form
90
+ * error text, and extract the provider's human reset hint when present.
91
+ *
92
+ * The pi SDK does NOT throw these — it records them as an assistant message with
93
+ * stopReason "error" and an errorMessage like "Codex usage limit reached (plus
94
+ * plan). Resets in ~3h.". Callers reading message metadata MUST gate on
95
+ * stopReason === "error" before trusting this, so a task whose own output merely
96
+ * mentions "rate limit" is never misclassified. Patterns mirror the SDK's own
97
+ * non-retryable-limit table. Deliberately excludes transient overloaded/5xx
98
+ * errors, which stay recoverable and keep retrying.
99
+ */
100
+ export declare function classifyProviderLimit(text: string | undefined): {
101
+ matched: boolean;
102
+ resetHint?: string;
103
+ };
104
+ /** Recognize abort-like Error messages without assuming a provider-specific class. */
105
+ export declare function isAbortError(error: unknown): boolean;
106
+ /** Recognize timeout-like errors by name or message. */
107
+ export declare function isTimeoutError(error: unknown): boolean;
108
+ /**
109
+ * Wrap an unknown error into a WorkflowError with appropriate classification.
110
+ */
111
+ export declare function wrapError(error: unknown, context?: {
112
+ agentLabel?: string;
113
+ }): WorkflowError;
package/dist/errors.js ADDED
@@ -0,0 +1,140 @@
1
+ /**
2
+ * Workflow-specific error types.
3
+ */
4
+ /** Stable runtime and persistence failure codes exposed to callers and UI surfaces. */
5
+ export var WorkflowErrorCode;
6
+ (function (WorkflowErrorCode) {
7
+ /** Agent exceeded timeout. */
8
+ WorkflowErrorCode["AGENT_TIMEOUT"] = "AGENT_TIMEOUT";
9
+ /** Workflow was aborted by user. */
10
+ WorkflowErrorCode["WORKFLOW_ABORTED"] = "WORKFLOW_ABORTED";
11
+ /** Agent limit exceeded. */
12
+ WorkflowErrorCode["AGENT_LIMIT_EXCEEDED"] = "AGENT_LIMIT_EXCEEDED";
13
+ /** Token budget exhausted. */
14
+ WorkflowErrorCode["TOKEN_BUDGET_EXHAUSTED"] = "TOKEN_BUDGET_EXHAUSTED";
15
+ /**
16
+ * The provider's subscription/usage/quota/rate limit was hit. Distinct from the
17
+ * user's self-imposed TOKEN_BUDGET_EXHAUSTED: a provider limit refills on its own,
18
+ * so the run is checkpointed (paused) and replayed by resume() rather than failed.
19
+ */
20
+ WorkflowErrorCode["PROVIDER_USAGE_LIMIT"] = "PROVIDER_USAGE_LIMIT";
21
+ /** Script validation failed. */
22
+ WorkflowErrorCode["SCRIPT_VALIDATION_ERROR"] = "SCRIPT_VALIDATION_ERROR";
23
+ /** A schema agent never produced valid structured_output (after repair + extraction). */
24
+ WorkflowErrorCode["SCHEMA_NONCOMPLIANCE"] = "SCHEMA_NONCOMPLIANCE";
25
+ /** A non-schema agent completed without any assistant text output. */
26
+ WorkflowErrorCode["AGENT_EMPTY_OUTPUT"] = "AGENT_EMPTY_OUTPUT";
27
+ /** Agent execution failed. */
28
+ WorkflowErrorCode["AGENT_EXECUTION_ERROR"] = "AGENT_EXECUTION_ERROR";
29
+ /** Run state persistence failed. */
30
+ WorkflowErrorCode["PERSISTENCE_ERROR"] = "PERSISTENCE_ERROR";
31
+ /** Unknown error. */
32
+ WorkflowErrorCode["UNKNOWN"] = "UNKNOWN";
33
+ })(WorkflowErrorCode || (WorkflowErrorCode = {}));
34
+ /** Classified workflow failure with recoverability and optional agent/provider context. */
35
+ export class WorkflowError extends Error {
36
+ code;
37
+ recoverable;
38
+ agentLabel;
39
+ details;
40
+ /** For PROVIDER_USAGE_LIMIT: the provider's human reset hint, e.g. "Resets in ~3h" (verbatim). */
41
+ resetHint;
42
+ constructor(message, code, options = {}) {
43
+ super(message);
44
+ this.name = "WorkflowError";
45
+ this.code = code;
46
+ this.recoverable = options.recoverable ?? false;
47
+ this.agentLabel = options.agentLabel;
48
+ this.details = options.details;
49
+ this.resetHint = options.resetHint;
50
+ }
51
+ }
52
+ /** Contract failure that retains every definition or assembly diagnostic. */
53
+ export class WorkflowCapabilityContractError extends Error {
54
+ diagnostics;
55
+ constructor(message, diagnostics) {
56
+ super(message);
57
+ this.name = "WorkflowCapabilityContractError";
58
+ this.diagnostics = diagnostics;
59
+ }
60
+ }
61
+ /** Generation failure that retains loading and token evidence for diagnosis. */
62
+ export class ModelGenerationError extends Error {
63
+ skillLoadingEvidence;
64
+ tokenUsage;
65
+ constructor(message, skillLoadingEvidence, tokenUsage) {
66
+ super(message);
67
+ this.name = "ModelGenerationError";
68
+ this.skillLoadingEvidence = skillLoadingEvidence;
69
+ this.tokenUsage = tokenUsage;
70
+ }
71
+ }
72
+ /** Narrow an unknown failure to WorkflowError. */
73
+ export function isWorkflowError(error) {
74
+ return error instanceof WorkflowError;
75
+ }
76
+ /** Report whether an unknown failure is a provider usage-limit checkpoint condition. */
77
+ export function isProviderUsageLimit(error) {
78
+ return isWorkflowError(error) && error.code === WorkflowErrorCode.PROVIDER_USAGE_LIMIT;
79
+ }
80
+ /**
81
+ * Detect a provider subscription/usage/quota/rate-limit exhaustion from free-form
82
+ * error text, and extract the provider's human reset hint when present.
83
+ *
84
+ * The pi SDK does NOT throw these — it records them as an assistant message with
85
+ * stopReason "error" and an errorMessage like "Codex usage limit reached (plus
86
+ * plan). Resets in ~3h.". Callers reading message metadata MUST gate on
87
+ * stopReason === "error" before trusting this, so a task whose own output merely
88
+ * mentions "rate limit" is never misclassified. Patterns mirror the SDK's own
89
+ * non-retryable-limit table. Deliberately excludes transient overloaded/5xx
90
+ * errors, which stay recoverable and keep retrying.
91
+ */
92
+ export function classifyProviderLimit(text) {
93
+ if (!text)
94
+ return { matched: false };
95
+ const matched = /usage limit|limit reached|insufficient[_\s]?quota|quota exceeded|exceeded your current quota|out of budget|available balance|\bquota\b|rate.?limit|too many requests|\b429\b|GoUsageLimitError|FreeUsageLimitError|\bbilling\b/i.test(text);
96
+ if (!matched)
97
+ return { matched: false };
98
+ const reset = text.match(/resets?\s+(?:in|at)\s+[^.\n]+/i);
99
+ return { matched: true, resetHint: reset?.[0]?.trim() };
100
+ }
101
+ /** Recognize abort-like Error messages without assuming a provider-specific class. */
102
+ export function isAbortError(error) {
103
+ if (!(error instanceof Error))
104
+ return false;
105
+ return /\babort(?:ed)?\b/i.test(error.message);
106
+ }
107
+ /** Recognize timeout-like errors by name or message. */
108
+ export function isTimeoutError(error) {
109
+ if (!(error instanceof Error))
110
+ return false;
111
+ return /\btimeout\b/i.test(error.message) || error.name === "TimeoutError";
112
+ }
113
+ /**
114
+ * Wrap an unknown error into a WorkflowError with appropriate classification.
115
+ */
116
+ export function wrapError(error, context) {
117
+ if (isWorkflowError(error))
118
+ return error;
119
+ if (isAbortError(error)) {
120
+ return new WorkflowError(error instanceof Error ? error.message : "Workflow was aborted", WorkflowErrorCode.WORKFLOW_ABORTED, { recoverable: true });
121
+ }
122
+ if (isTimeoutError(error)) {
123
+ return new WorkflowError(error instanceof Error ? error.message : "Agent timed out", WorkflowErrorCode.AGENT_TIMEOUT, { recoverable: true, agentLabel: context?.agentLabel });
124
+ }
125
+ // Defense-in-depth: today the SDK buries provider usage/quota limits in an
126
+ // assistant message (detected in agent.ts), but a future SDK might throw them.
127
+ // Classify a thrown limit here too — recoverable:false so the run checkpoints
128
+ // (paused) instead of being retried into the same wall or silently nulled.
129
+ if (error instanceof Error) {
130
+ const limit = classifyProviderLimit(error.message);
131
+ if (limit.matched) {
132
+ return new WorkflowError(error.message, WorkflowErrorCode.PROVIDER_USAGE_LIMIT, {
133
+ recoverable: false,
134
+ agentLabel: context?.agentLabel,
135
+ resetHint: limit.resetHint,
136
+ });
137
+ }
138
+ }
139
+ return new WorkflowError(error instanceof Error ? error.message : String(error), WorkflowErrorCode.AGENT_EXECUTION_ERROR, { recoverable: true, agentLabel: context?.agentLabel, details: error });
140
+ }
@@ -0,0 +1,37 @@
1
+ import type { EffortState } from "./effort-command.js";
2
+ import type { WorkflowManager } from "./workflow-manager.js";
3
+ /**
4
+ * Live extension state that Pi may hand from one extension generation to the
5
+ * next during `/reload`. This deliberately stays process-local: run snapshots
6
+ * and journals already provide the durable cold-start path, while the live
7
+ * manager is what owns in-flight promises, abort controllers, and event streams.
8
+ */
9
+ export declare const WORKFLOW_EXTENSION_VERSION: string;
10
+ export interface WorkflowReloadRuntime {
11
+ cwd: string;
12
+ /** Package version that created this manager. Only an exact match is retained. */
13
+ extensionVersion: string;
14
+ manager: WorkflowManager;
15
+ effort: EffortState;
16
+ }
17
+ export interface WorkflowRuntimeClaim {
18
+ compatible?: WorkflowReloadRuntime;
19
+ versionMismatch?: WorkflowReloadRuntime;
20
+ }
21
+ /** Stage a live runtime immediately before Pi tears down the old extension runner. */
22
+ export declare function handoffWorkflowRuntime(runtime: WorkflowReloadRuntime): void;
23
+ /** Claim a staged runtime from the extension generation that `/reload` just stopped. */
24
+ export declare function takeWorkflowRuntime(cwd: string): WorkflowReloadRuntime | undefined;
25
+ /**
26
+ * Claim a staged runtime and compare its package version with this extension
27
+ * generation. Any package update falls back to a fresh manager; only reloads
28
+ * within the exact same installed version retain live workflow state.
29
+ */
30
+ export declare function claimWorkflowRuntime(cwd: string): WorkflowRuntimeClaim;
31
+ /**
32
+ * Move live runs from a replaced extension version onto the existing journal
33
+ * recovery path before the fresh manager is constructed.
34
+ */
35
+ export declare function pauseVersionMismatchWorkflowRuntime(runtime: WorkflowReloadRuntime): number;
36
+ /** Test/cleanup helper; identity guard avoids deleting a newer handoff. */
37
+ export declare function discardWorkflowRuntime(cwd: string, runtime?: WorkflowReloadRuntime): void;
@@ -0,0 +1,78 @@
1
+ import packageJson from "../package.json" with { type: "json" };
2
+ /**
3
+ * Live extension state that Pi may hand from one extension generation to the
4
+ * next during `/reload`. This deliberately stays process-local: run snapshots
5
+ * and journals already provide the durable cold-start path, while the live
6
+ * manager is what owns in-flight promises, abort controllers, and event streams.
7
+ */
8
+ export const WORKFLOW_EXTENSION_VERSION = packageJson.version;
9
+ const RELOAD_HANDOFF_KEY = Symbol.for("@quintinshaw/pi-dynamic-workflows:reload-handoffs");
10
+ const RELOAD_HANDOFF_TTL_MS = 30_000;
11
+ function handoffs() {
12
+ const root = globalThis;
13
+ const existing = root[RELOAD_HANDOFF_KEY];
14
+ if (existing)
15
+ return existing;
16
+ const created = new Map();
17
+ root[RELOAD_HANDOFF_KEY] = created;
18
+ return created;
19
+ }
20
+ /** Stage a live runtime immediately before Pi tears down the old extension runner. */
21
+ export function handoffWorkflowRuntime(runtime) {
22
+ const store = handoffs();
23
+ const previous = store.get(runtime.cwd);
24
+ if (previous)
25
+ clearTimeout(previous.timer);
26
+ const entry = {};
27
+ entry.runtime = runtime;
28
+ entry.timer = setTimeout(() => {
29
+ if (store.get(runtime.cwd) === entry)
30
+ store.delete(runtime.cwd);
31
+ }, RELOAD_HANDOFF_TTL_MS);
32
+ entry.timer.unref?.();
33
+ store.set(runtime.cwd, entry);
34
+ }
35
+ /** Claim a staged runtime from the extension generation that `/reload` just stopped. */
36
+ export function takeWorkflowRuntime(cwd) {
37
+ const store = handoffs();
38
+ const entry = store.get(cwd);
39
+ if (!entry)
40
+ return undefined;
41
+ clearTimeout(entry.timer);
42
+ store.delete(cwd);
43
+ return entry.runtime;
44
+ }
45
+ /**
46
+ * Claim a staged runtime and compare its package version with this extension
47
+ * generation. Any package update falls back to a fresh manager; only reloads
48
+ * within the exact same installed version retain live workflow state.
49
+ */
50
+ export function claimWorkflowRuntime(cwd) {
51
+ const runtime = takeWorkflowRuntime(cwd);
52
+ if (!runtime)
53
+ return {};
54
+ return runtime.extensionVersion === WORKFLOW_EXTENSION_VERSION
55
+ ? { compatible: runtime }
56
+ : { versionMismatch: runtime };
57
+ }
58
+ /**
59
+ * Move live runs from a replaced extension version onto the existing journal
60
+ * recovery path before the fresh manager is constructed.
61
+ */
62
+ export function pauseVersionMismatchWorkflowRuntime(runtime) {
63
+ let paused = 0;
64
+ for (const run of runtime.manager.listRuns()) {
65
+ if (run.status === "running" && runtime.manager.pause(run.runId))
66
+ paused++;
67
+ }
68
+ return paused;
69
+ }
70
+ /** Test/cleanup helper; identity guard avoids deleting a newer handoff. */
71
+ export function discardWorkflowRuntime(cwd, runtime) {
72
+ const store = handoffs();
73
+ const entry = store.get(cwd);
74
+ if (!entry || (runtime && entry.runtime !== runtime))
75
+ return;
76
+ clearTimeout(entry.timer);
77
+ store.delete(cwd);
78
+ }
@@ -0,0 +1,63 @@
1
+ /**
2
+ * Shared filesystem primitives for JSON-backed persistence.
3
+ *
4
+ * Both run-persistence.ts (workflow runs) and workflow-saved.ts (saved
5
+ * workflow commands) persist plain-JSON records to per-record files under a
6
+ * project/user directory, and both need the same three guarantees:
7
+ *
8
+ * 1. Atomic writes with a recovery backup — a crash mid-write must never
9
+ * corrupt the live file, and a later-discovered-truncated primary must
10
+ * still be recoverable from the last good write.
11
+ * 2. Corrupt-file recovery on read — a truncated/corrupt primary falls back
12
+ * to its `.bak` sidecar instead of losing the record.
13
+ * 3. A missing or unreadable directory degrades to "no files" rather than
14
+ * throwing — a listing must never crash because one storage location is
15
+ * temporarily inaccessible (not yet created, deleted mid-race, EACCES).
16
+ *
17
+ * This module is the single implementation of all three; run-persistence.ts
18
+ * and workflow-saved.ts both call into it rather than maintaining parallel
19
+ * copies.
20
+ */
21
+ import { existsSync, mkdirSync, readdirSync, readFileSync, renameSync, statSync, unlinkSync, writeFileSync } from "node:fs";
22
+ /** Filesystem operations used by JSON persistence. Exposed for testing. */
23
+ export type PersistenceFsLayer = {
24
+ existsSync: typeof existsSync;
25
+ mkdirSync: typeof mkdirSync;
26
+ readdirSync: typeof readdirSync;
27
+ readFileSync: typeof readFileSync;
28
+ renameSync: typeof renameSync;
29
+ statSync: typeof statSync;
30
+ unlinkSync: typeof unlinkSync;
31
+ writeFileSync: typeof writeFileSync;
32
+ };
33
+ /** The real node:fs implementations. */
34
+ export declare function defaultPersistenceFs(): PersistenceFsLayer;
35
+ /** Merge a partial test override on top of the real node:fs implementations. */
36
+ export declare function resolvePersistenceFs(overrides?: Partial<PersistenceFsLayer>): PersistenceFsLayer;
37
+ /** Ensure `dir` exists (recursive mkdir), idempotent. */
38
+ export declare function ensureDir(fs: PersistenceFsLayer, dir: string): void;
39
+ /**
40
+ * Atomically write JSON to `path`: tmp-write + rename (atomic on the same
41
+ * filesystem, so a crash mid-write can't corrupt the live file), then
42
+ * best-effort refresh a `.bak` sidecar from the just-written good state —
43
+ * the recovery fallback readJsonWithBackupRecovery() uses if the primary is
44
+ * later found truncated (e.g. a rename that itself got interrupted by a
45
+ * power loss on a filesystem/OS combination where rename isn't fully atomic).
46
+ */
47
+ export declare function writeJsonAtomicWithBackup(fs: PersistenceFsLayer, path: string, data: unknown): void;
48
+ /**
49
+ * Read JSON from `path`, falling back to `path.bak` if the primary is
50
+ * missing or fails to parse. Returns null if neither candidate parses.
51
+ */
52
+ export declare function readJsonWithBackupRecovery<T>(fs: PersistenceFsLayer, path: string): T | null;
53
+ /**
54
+ * List `.json` record files in `dir`. A missing directory (never created
55
+ * yet) or an unreadable one (deleted between the existsSync check and
56
+ * readdirSync, permission-denied, etc.) both degrade to an empty list
57
+ * rather than throwing — callers (run listings, saved-workflow listings)
58
+ * must never crash a navigator/listing because one storage location is
59
+ * temporarily inaccessible.
60
+ */
61
+ export declare function listJsonFilesSafe(fs: PersistenceFsLayer, dir: string): string[];
62
+ /** Best-effort unlink; ignores missing-file/permission errors, reports whether it deleted anything. */
63
+ export declare function unlinkIfExistsSafe(fs: PersistenceFsLayer, path: string): boolean;