@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,102 @@
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
+ /** The real node:fs implementations. */
23
+ export function defaultPersistenceFs() {
24
+ return { existsSync, mkdirSync, readdirSync, readFileSync, renameSync, statSync, unlinkSync, writeFileSync };
25
+ }
26
+ /** Merge a partial test override on top of the real node:fs implementations. */
27
+ export function resolvePersistenceFs(overrides) {
28
+ const base = defaultPersistenceFs();
29
+ return overrides ? { ...base, ...overrides } : base;
30
+ }
31
+ /** Ensure `dir` exists (recursive mkdir), idempotent. */
32
+ export function ensureDir(fs, dir) {
33
+ if (!fs.existsSync(dir))
34
+ fs.mkdirSync(dir, { recursive: true });
35
+ }
36
+ /**
37
+ * Atomically write JSON to `path`: tmp-write + rename (atomic on the same
38
+ * filesystem, so a crash mid-write can't corrupt the live file), then
39
+ * best-effort refresh a `.bak` sidecar from the just-written good state —
40
+ * the recovery fallback readJsonWithBackupRecovery() uses if the primary is
41
+ * later found truncated (e.g. a rename that itself got interrupted by a
42
+ * power loss on a filesystem/OS combination where rename isn't fully atomic).
43
+ */
44
+ export function writeJsonAtomicWithBackup(fs, path, data) {
45
+ const json = JSON.stringify(data, null, 2);
46
+ fs.writeFileSync(`${path}.tmp`, json);
47
+ fs.renameSync(`${path}.tmp`, path);
48
+ try {
49
+ fs.writeFileSync(`${path}.bak`, json);
50
+ }
51
+ catch {
52
+ // Backup is best-effort; the primary write already succeeded.
53
+ }
54
+ }
55
+ /**
56
+ * Read JSON from `path`, falling back to `path.bak` if the primary is
57
+ * missing or fails to parse. Returns null if neither candidate parses.
58
+ */
59
+ export function readJsonWithBackupRecovery(fs, path) {
60
+ for (const candidate of [path, `${path}.bak`]) {
61
+ try {
62
+ if (!fs.existsSync(candidate))
63
+ continue;
64
+ return JSON.parse(fs.readFileSync(candidate, "utf-8"));
65
+ }
66
+ catch {
67
+ // Corrupt candidate -> fall through to the next candidate.
68
+ }
69
+ }
70
+ return null;
71
+ }
72
+ /**
73
+ * List `.json` record files in `dir`. A missing directory (never created
74
+ * yet) or an unreadable one (deleted between the existsSync check and
75
+ * readdirSync, permission-denied, etc.) both degrade to an empty list
76
+ * rather than throwing — callers (run listings, saved-workflow listings)
77
+ * must never crash a navigator/listing because one storage location is
78
+ * temporarily inaccessible.
79
+ */
80
+ export function listJsonFilesSafe(fs, dir) {
81
+ try {
82
+ if (!fs.existsSync(dir))
83
+ return [];
84
+ return fs.readdirSync(dir).filter((f) => f.endsWith(".json"));
85
+ }
86
+ catch {
87
+ return [];
88
+ }
89
+ }
90
+ /** Best-effort unlink; ignores missing-file/permission errors, reports whether it deleted anything. */
91
+ export function unlinkIfExistsSafe(fs, path) {
92
+ try {
93
+ if (fs.existsSync(path)) {
94
+ fs.unlinkSync(path);
95
+ return true;
96
+ }
97
+ }
98
+ catch {
99
+ // ignore
100
+ }
101
+ return false;
102
+ }
@@ -0,0 +1,57 @@
1
+ export type { AdversarialReviewConfig } from "./adversarial-review.js";
2
+ export { generateAdversarialReviewWorkflow, generateMultiPerspectiveWorkflow } from "./adversarial-review.js";
3
+ export type { AgentRunOptions, AgentRunResult, WorkflowAgentOptions } from "./agent.js";
4
+ export { listAvailableModelSpecs, listAvailableModels, WorkflowAgent } from "./agent.js";
5
+ export type { AgentHistoryEntry, AgentHistoryKind, AgentHistoryRole } from "./agent-history.js";
6
+ export { compactAgentHistory } from "./agent-history.js";
7
+ export type { AgentDefinition, AgentRegistry } from "./agent-registry.js";
8
+ export { applyToolPolicy, listAgentTypes, loadAgentRegistry, resolveAgentType } from "./agent-registry.js";
9
+ export { registerBuiltinWorkflows } from "./builtin-commands.js";
10
+ export { generateCodeReviewWorkflow, MAX_DIFF_CHARS } from "./code-review.js";
11
+ export * from "./config.js";
12
+ export type { DeepResearchConfig } from "./deep-research.js";
13
+ export { generateCodebaseAuditWorkflow, generateDeepResearchWorkflow } from "./deep-research.js";
14
+ export type { WorkflowAgentSnapshot, WorkflowAgentStatus, WorkflowDisplay, WorkflowDisplayOptions, WorkflowSnapshot, } from "./display.js";
15
+ export { createToolUpdateWorkflowDisplay, createWidgetWorkflowDisplay, createWorkflowSnapshot, preview, recomputeWorkflowSnapshot, renderWorkflowLines, renderWorkflowText, } from "./display.js";
16
+ export { createEffortState, type EffortLevel, type EffortState, effortDirective, isSubstantive, registerEffortCommand, } from "./effort-command.js";
17
+ export { isAbortError, isTimeoutError, isWorkflowError, WorkflowError, WorkflowErrorCode, wrapError, } from "./errors.js";
18
+ export type { WorkflowLogger, WorkflowLoggerOptions } from "./logger.js";
19
+ export { createWorkflowLogger } from "./logger.js";
20
+ export type { ModelRoute, ModelRoutingConfig } from "./model-routing.js";
21
+ export { parseModelRoutingFromMeta, resolveModelForPhase } from "./model-routing.js";
22
+ export type { ModelThinkingLevel, ResolvedModelSpec } from "./model-spec.js";
23
+ export { canonicalModelSpec, formatModelSpecWithThinking, isThinkingLevel, resolveModelSpecWithThinking, splitModelSpecThinking, THINKING_LEVELS, } from "./model-spec.js";
24
+ export type { ModelTierConfig, RankableModel } from "./model-tier-config.js";
25
+ export { buildDefaultTierConfig, formatTierFallbackNotice, getModelTierConfigPath, loadModelTierConfig, resolveTierModel, saveModelTierConfig, sortedTierNames, } from "./model-tier-config.js";
26
+ export type { PersistedRunState, RunPersistence, RunStatus } from "./run-persistence.js";
27
+ export { createRunPersistence, generateRunId } from "./run-persistence.js";
28
+ export { parseCommandArgs, registerAllSavedWorkflows, registerSavedWorkflow, } from "./saved-commands.js";
29
+ export { SharedStore } from "./shared-store.js";
30
+ export type { StructuredOutputCapture, StructuredOutputToolOptions } from "./structured-output.js";
31
+ export { createStructuredOutputTool } from "./structured-output.js";
32
+ export { deliverText, installResultDelivery, installTaskPanel, type TaskPanelOptions } from "./task-panel.js";
33
+ export type { AutoResumeDelayParams, SchedulableWorkflowManager, TimerHandle, UsageLimitSchedulerOptions, } from "./usage-limit-scheduler.js";
34
+ export { computeAutoResumeDelayMs, parseResetHintMs, UsageLimitScheduler } from "./usage-limit-scheduler.js";
35
+ export { createWebFetchTool, createWebSearchTool, createWebTools } from "./web-tools.js";
36
+ export type { AgentOptions, JournalEntry, SharedRuntime, WorkflowMeta, WorkflowMetaPhase, WorkflowRunOptions, WorkflowRunResult, } from "./workflow.js";
37
+ export { parseWorkflowScript, runWorkflow } from "./workflow.js";
38
+ export type { AlignmentEvidence, CapabilityDescriptor, CapabilityDiagnostic, DynamicReferenceDescriptor, OptionDescriptor, OptionShape, PresentAtVersion, RuntimeBindingAssembly, StaticCapabilityFact, WorkflowCapabilityContract, WorkflowCapabilityDefinition, WorkflowRuntimeImplementations, } from "./workflow-capability-contract.js";
39
+ export { CapabilityClassification, CapabilityOrigin, CapabilitySupport, DiagnosticSeverity, DiscoveryPlacement, defineWorkflowCapabilityContract, WORKFLOW_CAPABILITY_CONTRACT, WORKFLOW_CAPABILITY_DEFINITION, WorkflowCapabilityContractError, } from "./workflow-capability-contract.js";
40
+ export { registerWorkflowCommands } from "./workflow-commands.js";
41
+ export type { WorkflowControlInput, WorkflowControlRunDetails, WorkflowControlToolOptions, } from "./workflow-control-tool.js";
42
+ export { createWorkflowControlTool } from "./workflow-control-tool.js";
43
+ export { type ArmReason, buildArmedWorkflowPrompt, buildForcedWorkflowPrompt, endsWithTrigger, hasTrigger, type InstallWorkflowKeywordArmingOptions, installWorkflowKeywordArming, registerWorkflowProgressCommands, registerWorkflowTriggerCommand, type WorkflowModeState, } from "./workflow-editor.js";
44
+ export type { ManagedRun, WorkflowManagerOptions } from "./workflow-manager.js";
45
+ export { WorkflowManager } from "./workflow-manager.js";
46
+ export type { WorkflowProjectPaths } from "./workflow-paths.js";
47
+ export { WORKFLOW_HOME_RELATIVE_DIR, WORKFLOW_PROJECTS_SUBDIR, workflowHomeDir, workflowProjectKey, workflowProjectPaths, workflowUserSavedDir, } from "./workflow-paths.js";
48
+ export type { SavedWorkflow, WorkflowStorage } from "./workflow-saved.js";
49
+ export { assertSafeSavedWorkflowName, createWorkflowStorage, isSafeSavedWorkflowName } from "./workflow-saved.js";
50
+ export type { WorkflowSettings, WorkflowSettingsOptions, WorkflowSettingsStore } from "./workflow-settings.js";
51
+ export { getWorkflowProjectSettingsPath, getWorkflowSettingsPath, loadWorkflowSettings, saveWorkflowSettings, saveWorkflowSettingsForCwd, } from "./workflow-settings.js";
52
+ export type { WorkflowToolInput, WorkflowToolOptions } from "./workflow-tool.js";
53
+ export { backgroundStartedText, createWorkflowTool } from "./workflow-tool.js";
54
+ export { keyToAction, type NavAction, NavigatorModel, NavigatorState, openWorkflowNavigator, renderNavigator, type ViewKind, } from "./workflow-ui.js";
55
+ export { registerWorkflowModelsCommand } from "./workflows-models-command.js";
56
+ export type { Worktree } from "./worktree.js";
57
+ export { createWorktree, removeWorktree } from "./worktree.js";
package/dist/index.js ADDED
@@ -0,0 +1,35 @@
1
+ export { generateAdversarialReviewWorkflow, generateMultiPerspectiveWorkflow } from "./adversarial-review.js";
2
+ export { listAvailableModelSpecs, listAvailableModels, WorkflowAgent } from "./agent.js";
3
+ export { compactAgentHistory } from "./agent-history.js";
4
+ export { applyToolPolicy, listAgentTypes, loadAgentRegistry, resolveAgentType } from "./agent-registry.js";
5
+ export { registerBuiltinWorkflows } from "./builtin-commands.js";
6
+ export { generateCodeReviewWorkflow, MAX_DIFF_CHARS } from "./code-review.js";
7
+ export * from "./config.js";
8
+ export { generateCodebaseAuditWorkflow, generateDeepResearchWorkflow } from "./deep-research.js";
9
+ export { createToolUpdateWorkflowDisplay, createWidgetWorkflowDisplay, createWorkflowSnapshot, preview, recomputeWorkflowSnapshot, renderWorkflowLines, renderWorkflowText, } from "./display.js";
10
+ export { createEffortState, effortDirective, isSubstantive, registerEffortCommand, } from "./effort-command.js";
11
+ export { isAbortError, isTimeoutError, isWorkflowError, WorkflowError, WorkflowErrorCode, wrapError, } from "./errors.js";
12
+ export { createWorkflowLogger } from "./logger.js";
13
+ export { parseModelRoutingFromMeta, resolveModelForPhase } from "./model-routing.js";
14
+ export { canonicalModelSpec, formatModelSpecWithThinking, isThinkingLevel, resolveModelSpecWithThinking, splitModelSpecThinking, THINKING_LEVELS, } from "./model-spec.js";
15
+ export { buildDefaultTierConfig, formatTierFallbackNotice, getModelTierConfigPath, loadModelTierConfig, resolveTierModel, saveModelTierConfig, sortedTierNames, } from "./model-tier-config.js";
16
+ export { createRunPersistence, generateRunId } from "./run-persistence.js";
17
+ export { parseCommandArgs, registerAllSavedWorkflows, registerSavedWorkflow, } from "./saved-commands.js";
18
+ export { SharedStore } from "./shared-store.js";
19
+ export { createStructuredOutputTool } from "./structured-output.js";
20
+ export { deliverText, installResultDelivery, installTaskPanel } from "./task-panel.js";
21
+ export { computeAutoResumeDelayMs, parseResetHintMs, UsageLimitScheduler } from "./usage-limit-scheduler.js";
22
+ export { createWebFetchTool, createWebSearchTool, createWebTools } from "./web-tools.js";
23
+ export { parseWorkflowScript, runWorkflow } from "./workflow.js";
24
+ export { CapabilityClassification, CapabilityOrigin, CapabilitySupport, DiagnosticSeverity, DiscoveryPlacement, defineWorkflowCapabilityContract, WORKFLOW_CAPABILITY_CONTRACT, WORKFLOW_CAPABILITY_DEFINITION, WorkflowCapabilityContractError, } from "./workflow-capability-contract.js";
25
+ export { registerWorkflowCommands } from "./workflow-commands.js";
26
+ export { createWorkflowControlTool } from "./workflow-control-tool.js";
27
+ export { buildArmedWorkflowPrompt, buildForcedWorkflowPrompt, endsWithTrigger, hasTrigger, installWorkflowKeywordArming, registerWorkflowProgressCommands, registerWorkflowTriggerCommand, } from "./workflow-editor.js";
28
+ export { WorkflowManager } from "./workflow-manager.js";
29
+ export { WORKFLOW_HOME_RELATIVE_DIR, WORKFLOW_PROJECTS_SUBDIR, workflowHomeDir, workflowProjectKey, workflowProjectPaths, workflowUserSavedDir, } from "./workflow-paths.js";
30
+ export { assertSafeSavedWorkflowName, createWorkflowStorage, isSafeSavedWorkflowName } from "./workflow-saved.js";
31
+ export { getWorkflowProjectSettingsPath, getWorkflowSettingsPath, loadWorkflowSettings, saveWorkflowSettings, saveWorkflowSettingsForCwd, } from "./workflow-settings.js";
32
+ export { backgroundStartedText, createWorkflowTool } from "./workflow-tool.js";
33
+ export { keyToAction, NavigatorModel, NavigatorState, openWorkflowNavigator, renderNavigator, } from "./workflow-ui.js";
34
+ export { registerWorkflowModelsCommand } from "./workflows-models-command.js";
35
+ export { createWorktree, removeWorktree } from "./worktree.js";
@@ -0,0 +1,21 @@
1
+ /**
2
+ * Workflow logger with file persistence.
3
+ */
4
+ export interface WorkflowLogger {
5
+ log(message: string): void;
6
+ error(message: string): void;
7
+ warn(message: string): void;
8
+ getLogs(): string[];
9
+ persist(): string | null;
10
+ }
11
+ export interface WorkflowLoggerOptions {
12
+ /** Run ID for persistence. */
13
+ runId?: string;
14
+ /** Working directory for file paths. */
15
+ cwd?: string;
16
+ /** Whether to persist logs to disk. */
17
+ persist?: boolean;
18
+ /** Callback for each log entry. */
19
+ onLog?: (message: string) => void;
20
+ }
21
+ export declare function createWorkflowLogger(options?: WorkflowLoggerOptions): WorkflowLogger;
package/dist/logger.js ADDED
@@ -0,0 +1,66 @@
1
+ /**
2
+ * Workflow logger with file persistence.
3
+ */
4
+ import { appendFileSync, mkdirSync, writeFileSync } from "node:fs";
5
+ import { join } from "node:path";
6
+ import { workflowProjectPaths } from "./workflow-paths.js";
7
+ export function createWorkflowLogger(options = {}) {
8
+ const logs = [];
9
+ const persistLogs = options.persist ?? true;
10
+ const cwd = options.cwd ?? process.cwd();
11
+ const runId = options.runId ?? `run-${Date.now()}`;
12
+ const runsDir = workflowProjectPaths(cwd).runsDir;
13
+ let logFile = null;
14
+ const write = (level, message) => {
15
+ const timestamp = new Date().toISOString();
16
+ const entry = `[${timestamp}] [${level}] ${message}`;
17
+ logs.push(entry);
18
+ options.onLog?.(message);
19
+ if (persistLogs && logFile) {
20
+ try {
21
+ appendFileSync(logFile, `${entry}\n`);
22
+ }
23
+ catch {
24
+ // Silent fail for log persistence
25
+ }
26
+ }
27
+ };
28
+ const logger = {
29
+ log(message) {
30
+ write("INFO", message);
31
+ },
32
+ error(message) {
33
+ write("ERROR", message);
34
+ },
35
+ warn(message) {
36
+ write("WARN", message);
37
+ },
38
+ getLogs() {
39
+ return [...logs];
40
+ },
41
+ persist() {
42
+ if (!persistLogs)
43
+ return null;
44
+ try {
45
+ mkdirSync(runsDir, { recursive: true });
46
+ logFile = join(runsDir, `${runId}.log`);
47
+ writeFileSync(logFile, `${logs.join("\n")}\n`);
48
+ return logFile;
49
+ }
50
+ catch {
51
+ return null;
52
+ }
53
+ },
54
+ };
55
+ // Initialize log file if persisting
56
+ if (persistLogs) {
57
+ try {
58
+ mkdirSync(runsDir, { recursive: true });
59
+ logFile = join(runsDir, `${runId}.log`);
60
+ }
61
+ catch {
62
+ // Silent fail
63
+ }
64
+ }
65
+ return logger;
66
+ }
@@ -0,0 +1,30 @@
1
+ /**
2
+ * Per-stage model routing for workflows.
3
+ * Allows different phases to use different models.
4
+ */
5
+ export interface ModelRoute {
6
+ /** Phase name pattern (regex or exact match). */
7
+ phasePattern: string;
8
+ /** Model to use for this phase. */
9
+ model: string;
10
+ /** Whether to use regex matching. */
11
+ useRegex?: boolean;
12
+ }
13
+ export interface ModelRoutingConfig {
14
+ /** Default model for all phases. */
15
+ defaultModel?: string;
16
+ /** Per-phase model overrides. */
17
+ routes: ModelRoute[];
18
+ }
19
+ /**
20
+ * Resolve which model to use for a given phase.
21
+ */
22
+ export declare function resolveModelForPhase(phase: string | undefined, config: ModelRoutingConfig): string | undefined;
23
+ /**
24
+ * Parse model routing from workflow meta: per-phase models from meta.phases[].model
25
+ * and a top-level default from meta.model (used when no phase route matches).
26
+ */
27
+ export declare function parseModelRoutingFromMeta(phases?: Array<{
28
+ title: string;
29
+ model?: string;
30
+ }>, defaultModel?: string): ModelRoutingConfig;
@@ -0,0 +1,50 @@
1
+ /**
2
+ * Per-stage model routing for workflows.
3
+ * Allows different phases to use different models.
4
+ */
5
+ /**
6
+ * Resolve which model to use for a given phase.
7
+ */
8
+ export function resolveModelForPhase(phase, config) {
9
+ if (!phase || !config.routes.length) {
10
+ return config.defaultModel;
11
+ }
12
+ for (const route of config.routes) {
13
+ if (route.useRegex) {
14
+ try {
15
+ const regex = new RegExp(route.phasePattern, "i");
16
+ if (regex.test(phase)) {
17
+ return route.model;
18
+ }
19
+ }
20
+ catch {
21
+ // Invalid regex, skip
22
+ }
23
+ }
24
+ else if (phase === route.phasePattern) {
25
+ // Exact, case-sensitive match — phase titles are author-controlled literals,
26
+ // so fuzzy substring matching only caused mis-routes (e.g. "analyze" matching
27
+ // "analyze-deep" or vice-versa). Use the regex branch for fuzzy needs.
28
+ return route.model;
29
+ }
30
+ }
31
+ return config.defaultModel;
32
+ }
33
+ /**
34
+ * Parse model routing from workflow meta: per-phase models from meta.phases[].model
35
+ * and a top-level default from meta.model (used when no phase route matches).
36
+ */
37
+ export function parseModelRoutingFromMeta(phases, defaultModel) {
38
+ const routes = [];
39
+ if (phases) {
40
+ for (const phase of phases) {
41
+ if (phase.model) {
42
+ routes.push({
43
+ phasePattern: phase.title,
44
+ model: phase.model,
45
+ });
46
+ }
47
+ }
48
+ }
49
+ return { defaultModel, routes };
50
+ }
@@ -0,0 +1,29 @@
1
+ import type { Api, Model } from "@earendil-works/pi-ai";
2
+ import type { ModelRegistry } from "@earendil-works/pi-coding-agent";
3
+ export declare const THINKING_LEVELS: readonly ["off", "minimal", "low", "medium", "high", "xhigh", "max"];
4
+ export type ModelThinkingLevel = (typeof THINKING_LEVELS)[number];
5
+ export interface ResolvedModelSpec {
6
+ requestedSpec: string;
7
+ model?: Model<Api>;
8
+ thinkingLevel?: ModelThinkingLevel;
9
+ resolvedSpec?: string;
10
+ warning?: string;
11
+ error?: string;
12
+ }
13
+ export declare function isThinkingLevel(value: string): value is ModelThinkingLevel;
14
+ export declare function formatModelSpecWithThinking(modelSpec: string, thinkingLevel: ModelThinkingLevel | undefined): string;
15
+ export declare function canonicalModelSpec(model: Model<Api>): string;
16
+ /**
17
+ * Split a stored tier spec for display/editing. Exact known model specs win, so
18
+ * model ids that legitimately contain colons are not mistaken for thinking.
19
+ */
20
+ export declare function splitModelSpecThinking(spec: string | undefined, knownModelSpecs?: readonly string[]): {
21
+ modelSpec: string;
22
+ thinkingLevel?: ModelThinkingLevel;
23
+ };
24
+ /**
25
+ * Resolve a workflow model-tier/agent model string with the same user-facing
26
+ * grammar as Pi CLI `--model`: `provider/modelId[:thinking]`, bare model ids,
27
+ * fuzzy patterns, and exact colon-containing model ids.
28
+ */
29
+ export declare function resolveModelSpecWithThinking(spec: string, modelRegistry: Pick<ModelRegistry, "getAll">): ResolvedModelSpec;
@@ -0,0 +1,252 @@
1
+ export const THINKING_LEVELS = ["off", "minimal", "low", "medium", "high", "xhigh", "max"];
2
+ const DEFAULT_MODEL_PER_PROVIDER = {
3
+ "amazon-bedrock": "us.anthropic.claude-opus-4-6-v1",
4
+ anthropic: "claude-opus-4-8",
5
+ openai: "gpt-5.4",
6
+ "azure-openai-responses": "gpt-5.4",
7
+ "openai-codex": "gpt-5.5",
8
+ deepseek: "deepseek-v4-pro",
9
+ google: "gemini-3.1-pro-preview",
10
+ "google-vertex": "gemini-3.1-pro-preview",
11
+ "github-copilot": "gpt-5.4",
12
+ openrouter: "moonshotai/kimi-k2.6",
13
+ "vercel-ai-gateway": "zai/glm-5.1",
14
+ zai: "glm-5.1",
15
+ mistral: "devstral-medium-latest",
16
+ minimax: "MiniMax-M2.7",
17
+ "minimax-cn": "MiniMax-M2.7",
18
+ moonshotai: "kimi-k2.6",
19
+ "moonshotai-cn": "kimi-k2.6",
20
+ huggingface: "moonshotai/Kimi-K2.6",
21
+ fireworks: "accounts/fireworks/models/kimi-k2p6",
22
+ together: "moonshotai/Kimi-K2.6",
23
+ opencode: "kimi-k2.6",
24
+ "opencode-go": "kimi-k2.6",
25
+ "kimi-coding": "kimi-for-coding",
26
+ "cloudflare-workers-ai": "@cf/moonshotai/kimi-k2.6",
27
+ "cloudflare-ai-gateway": "workers-ai/@cf/moonshotai/kimi-k2.6",
28
+ xiaomi: "mimo-v2.5-pro",
29
+ "xiaomi-token-plan-cn": "mimo-v2.5-pro",
30
+ "xiaomi-token-plan-ams": "mimo-v2.5-pro",
31
+ "xiaomi-token-plan-sgp": "mimo-v2.5-pro",
32
+ };
33
+ export function isThinkingLevel(value) {
34
+ return THINKING_LEVELS.includes(value);
35
+ }
36
+ export function formatModelSpecWithThinking(modelSpec, thinkingLevel) {
37
+ return thinkingLevel ? `${modelSpec}:${thinkingLevel}` : modelSpec;
38
+ }
39
+ export function canonicalModelSpec(model) {
40
+ return `${model.provider}/${model.id}`;
41
+ }
42
+ /**
43
+ * Split a stored tier spec for display/editing. Exact known model specs win, so
44
+ * model ids that legitimately contain colons are not mistaken for thinking.
45
+ */
46
+ export function splitModelSpecThinking(spec, knownModelSpecs) {
47
+ const trimmed = spec?.trim() ?? "";
48
+ if (!trimmed)
49
+ return { modelSpec: "", thinkingLevel: undefined };
50
+ const known = knownModelSpecs ? new Set(knownModelSpecs) : undefined;
51
+ if (known?.has(trimmed))
52
+ return { modelSpec: trimmed, thinkingLevel: undefined };
53
+ const lastColon = trimmed.lastIndexOf(":");
54
+ if (lastColon === -1)
55
+ return { modelSpec: trimmed, thinkingLevel: undefined };
56
+ const prefix = trimmed.slice(0, lastColon);
57
+ const suffix = trimmed.slice(lastColon + 1);
58
+ if (!prefix || !isThinkingLevel(suffix))
59
+ return { modelSpec: trimmed, thinkingLevel: undefined };
60
+ if (known && !known.has(prefix))
61
+ return { modelSpec: trimmed, thinkingLevel: undefined };
62
+ return { modelSpec: prefix, thinkingLevel: suffix };
63
+ }
64
+ function isAlias(id) {
65
+ if (id.endsWith("-latest"))
66
+ return true;
67
+ return !/-\d{8}$/.test(id);
68
+ }
69
+ function findExactModelReferenceMatch(modelReference, availableModels) {
70
+ const trimmedReference = modelReference.trim();
71
+ if (!trimmedReference)
72
+ return undefined;
73
+ const normalizedReference = trimmedReference.toLowerCase();
74
+ const canonicalMatches = availableModels.filter((model) => canonicalModelSpec(model).toLowerCase() === normalizedReference);
75
+ if (canonicalMatches.length === 1)
76
+ return canonicalMatches[0];
77
+ if (canonicalMatches.length > 1)
78
+ return undefined;
79
+ const slashIndex = trimmedReference.indexOf("/");
80
+ if (slashIndex !== -1) {
81
+ const provider = trimmedReference.slice(0, slashIndex).trim();
82
+ const modelId = trimmedReference.slice(slashIndex + 1).trim();
83
+ if (provider && modelId) {
84
+ const providerMatches = availableModels.filter((model) => model.provider.toLowerCase() === provider.toLowerCase() && model.id.toLowerCase() === modelId.toLowerCase());
85
+ if (providerMatches.length === 1)
86
+ return providerMatches[0];
87
+ if (providerMatches.length > 1)
88
+ return undefined;
89
+ }
90
+ }
91
+ const idMatches = availableModels.filter((model) => model.id.toLowerCase() === normalizedReference);
92
+ return idMatches.length === 1 ? idMatches[0] : undefined;
93
+ }
94
+ function tryMatchModel(modelPattern, availableModels) {
95
+ const exactMatch = findExactModelReferenceMatch(modelPattern, availableModels);
96
+ if (exactMatch)
97
+ return exactMatch;
98
+ const normalizedPattern = modelPattern.toLowerCase();
99
+ const matches = availableModels.filter((model) => model.id.toLowerCase().includes(normalizedPattern) || model.name?.toLowerCase().includes(normalizedPattern));
100
+ if (matches.length === 0)
101
+ return undefined;
102
+ const aliases = matches.filter((model) => isAlias(model.id));
103
+ if (aliases.length > 0) {
104
+ aliases.sort((a, b) => b.id.localeCompare(a.id));
105
+ return aliases[0];
106
+ }
107
+ const datedVersions = matches.filter((model) => !isAlias(model.id));
108
+ datedVersions.sort((a, b) => b.id.localeCompare(a.id));
109
+ return datedVersions[0];
110
+ }
111
+ function parseModelPattern(pattern, availableModels, options) {
112
+ const exactMatch = tryMatchModel(pattern, availableModels);
113
+ if (exactMatch)
114
+ return { model: exactMatch };
115
+ const lastColonIndex = pattern.lastIndexOf(":");
116
+ if (lastColonIndex === -1)
117
+ return {};
118
+ const prefix = pattern.slice(0, lastColonIndex);
119
+ const suffix = pattern.slice(lastColonIndex + 1);
120
+ if (isThinkingLevel(suffix)) {
121
+ const result = parseModelPattern(prefix, availableModels, options);
122
+ if (!result.model)
123
+ return result;
124
+ return {
125
+ model: result.model,
126
+ thinkingLevel: result.warning ? undefined : suffix,
127
+ warning: result.warning,
128
+ };
129
+ }
130
+ if (options?.allowInvalidThinkingLevelFallback === false)
131
+ return {};
132
+ const result = parseModelPattern(prefix, availableModels, options);
133
+ if (!result.model)
134
+ return result;
135
+ return {
136
+ model: result.model,
137
+ warning: `Invalid thinking level "${suffix}" in pattern "${pattern}". Using default instead.`,
138
+ };
139
+ }
140
+ function buildFallbackModel(provider, modelId, availableModels) {
141
+ const providerModels = availableModels.filter((model) => model.provider === provider);
142
+ if (providerModels.length === 0)
143
+ return undefined;
144
+ const defaultId = DEFAULT_MODEL_PER_PROVIDER[provider];
145
+ const baseModel = defaultId
146
+ ? (providerModels.find((model) => model.id === defaultId) ?? providerModels[0])
147
+ : providerModels[0];
148
+ return { ...baseModel, id: modelId, name: modelId };
149
+ }
150
+ /**
151
+ * Resolve a workflow model-tier/agent model string with the same user-facing
152
+ * grammar as Pi CLI `--model`: `provider/modelId[:thinking]`, bare model ids,
153
+ * fuzzy patterns, and exact colon-containing model ids.
154
+ */
155
+ export function resolveModelSpecWithThinking(spec, modelRegistry) {
156
+ const requestedSpec = spec.trim();
157
+ if (!requestedSpec)
158
+ return { requestedSpec, error: "No model spec provided." };
159
+ const availableModels = modelRegistry.getAll();
160
+ if (availableModels.length === 0) {
161
+ return {
162
+ requestedSpec,
163
+ error: "No models available. Check your installation or add models to models.json.",
164
+ };
165
+ }
166
+ const providerMap = new Map();
167
+ for (const model of availableModels) {
168
+ providerMap.set(model.provider.toLowerCase(), model.provider);
169
+ }
170
+ let provider;
171
+ let pattern = requestedSpec;
172
+ let inferredProvider = false;
173
+ const slashIndex = requestedSpec.indexOf("/");
174
+ if (slashIndex !== -1) {
175
+ const maybeProvider = requestedSpec.slice(0, slashIndex);
176
+ const canonicalProvider = providerMap.get(maybeProvider.toLowerCase());
177
+ if (canonicalProvider) {
178
+ provider = canonicalProvider;
179
+ pattern = requestedSpec.slice(slashIndex + 1);
180
+ inferredProvider = true;
181
+ }
182
+ }
183
+ if (!provider) {
184
+ const exact = findExactModelReferenceMatch(requestedSpec, availableModels);
185
+ if (exact) {
186
+ return { requestedSpec, model: exact, resolvedSpec: canonicalModelSpec(exact) };
187
+ }
188
+ }
189
+ const candidates = provider ? availableModels.filter((model) => model.provider === provider) : availableModels;
190
+ const { model, thinkingLevel, warning } = parseModelPattern(pattern, candidates, {
191
+ allowInvalidThinkingLevelFallback: false,
192
+ });
193
+ if (model) {
194
+ return {
195
+ requestedSpec,
196
+ model,
197
+ thinkingLevel,
198
+ warning,
199
+ resolvedSpec: formatModelSpecWithThinking(canonicalModelSpec(model), thinkingLevel),
200
+ };
201
+ }
202
+ if (inferredProvider) {
203
+ const exact = findExactModelReferenceMatch(requestedSpec, availableModels);
204
+ if (exact) {
205
+ return { requestedSpec, model: exact, resolvedSpec: canonicalModelSpec(exact) };
206
+ }
207
+ const fallback = parseModelPattern(requestedSpec, availableModels, {
208
+ allowInvalidThinkingLevelFallback: false,
209
+ });
210
+ if (fallback.model) {
211
+ return {
212
+ requestedSpec,
213
+ model: fallback.model,
214
+ thinkingLevel: fallback.thinkingLevel,
215
+ warning: fallback.warning,
216
+ resolvedSpec: formatModelSpecWithThinking(canonicalModelSpec(fallback.model), fallback.thinkingLevel),
217
+ };
218
+ }
219
+ }
220
+ if (provider) {
221
+ let fallbackPattern = pattern;
222
+ let fallbackThinking;
223
+ const lastColon = pattern.lastIndexOf(":");
224
+ if (lastColon !== -1) {
225
+ const suffix = pattern.slice(lastColon + 1);
226
+ if (isThinkingLevel(suffix)) {
227
+ fallbackPattern = pattern.slice(0, lastColon);
228
+ fallbackThinking = suffix;
229
+ }
230
+ }
231
+ const fallbackModel = buildFallbackModel(provider, fallbackPattern, availableModels);
232
+ if (fallbackModel) {
233
+ const modelWithReasoning = fallbackThinking && fallbackThinking !== "off" ? { ...fallbackModel, reasoning: true } : fallbackModel;
234
+ const fallbackWarning = warning
235
+ ? `${warning} Model "${fallbackPattern}" not found for provider "${provider}". Using custom model id.`
236
+ : `Model "${fallbackPattern}" not found for provider "${provider}". Using custom model id.`;
237
+ return {
238
+ requestedSpec,
239
+ model: modelWithReasoning,
240
+ thinkingLevel: fallbackThinking,
241
+ warning: fallbackWarning,
242
+ resolvedSpec: formatModelSpecWithThinking(canonicalModelSpec(modelWithReasoning), fallbackThinking),
243
+ };
244
+ }
245
+ }
246
+ const display = provider ? `${provider}/${pattern}` : requestedSpec;
247
+ return {
248
+ requestedSpec,
249
+ warning,
250
+ error: `Model "${display}" not found. Use /workflows-models to choose an available model.`,
251
+ };
252
+ }