@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.
- package/LICENSE +22 -0
- package/README.md +325 -0
- package/assets/readme/hero.png +0 -0
- package/assets/readme/hero.svg +70 -0
- package/assets/readme/package-cover.png +0 -0
- package/assets/readme/package-cover.svg +66 -0
- package/assets/readme/workflow.png +0 -0
- package/assets/readme/workflow.svg +77 -0
- package/dist/accept-workflow-guidance.d.ts +14 -0
- package/dist/accept-workflow-guidance.js +53 -0
- package/dist/adversarial-review.d.ts +30 -0
- package/dist/adversarial-review.js +107 -0
- package/dist/agent-history.d.ts +20 -0
- package/dist/agent-history.js +122 -0
- package/dist/agent-registry.d.ts +83 -0
- package/dist/agent-registry.js +190 -0
- package/dist/agent.d.ts +364 -0
- package/dist/agent.js +714 -0
- package/dist/builtin-commands.d.ts +19 -0
- package/dist/builtin-commands.js +251 -0
- package/dist/builtin-workflows.d.ts +45 -0
- package/dist/builtin-workflows.js +121 -0
- package/dist/code-review.d.ts +26 -0
- package/dist/code-review.js +181 -0
- package/dist/config.d.ts +37 -0
- package/dist/config.js +44 -0
- package/dist/deep-research.d.ts +30 -0
- package/dist/deep-research.js +124 -0
- package/dist/display.d.ts +134 -0
- package/dist/display.js +248 -0
- package/dist/effort-command.d.ts +28 -0
- package/dist/effort-command.js +68 -0
- package/dist/enums.d.ts +69 -0
- package/dist/enums.js +78 -0
- package/dist/errors.d.ts +113 -0
- package/dist/errors.js +140 -0
- package/dist/extension-reload.d.ts +37 -0
- package/dist/extension-reload.js +78 -0
- package/dist/fs-persistence.d.ts +63 -0
- package/dist/fs-persistence.js +102 -0
- package/dist/index.d.ts +57 -0
- package/dist/index.js +35 -0
- package/dist/logger.d.ts +21 -0
- package/dist/logger.js +66 -0
- package/dist/model-routing.d.ts +30 -0
- package/dist/model-routing.js +50 -0
- package/dist/model-spec.d.ts +29 -0
- package/dist/model-spec.js +252 -0
- package/dist/model-tier-config.d.ts +133 -0
- package/dist/model-tier-config.js +249 -0
- package/dist/run-persistence.d.ts +180 -0
- package/dist/run-persistence.js +294 -0
- package/dist/saved-commands.d.ts +28 -0
- package/dist/saved-commands.js +100 -0
- package/dist/shared-store.d.ts +98 -0
- package/dist/shared-store.js +212 -0
- package/dist/structured-output.d.ts +19 -0
- package/dist/structured-output.js +30 -0
- package/dist/task-panel.d.ts +61 -0
- package/dist/task-panel.js +422 -0
- package/dist/usage-limit-scheduler.d.ts +145 -0
- package/dist/usage-limit-scheduler.js +368 -0
- package/dist/web-tools.d.ts +20 -0
- package/dist/web-tools.js +120 -0
- package/dist/workflow-authoring-coverage.d.ts +70 -0
- package/dist/workflow-authoring-coverage.js +421 -0
- package/dist/workflow-authoring-reference.d.ts +20 -0
- package/dist/workflow-authoring-reference.js +156 -0
- package/dist/workflow-capability-contract.d.ts +131 -0
- package/dist/workflow-capability-contract.js +604 -0
- package/dist/workflow-commands.d.ts +18 -0
- package/dist/workflow-commands.js +260 -0
- package/dist/workflow-comprehension.d.ts +133 -0
- package/dist/workflow-comprehension.js +1321 -0
- package/dist/workflow-context-measurement.d.ts +72 -0
- package/dist/workflow-context-measurement.js +213 -0
- package/dist/workflow-control-tool.d.ts +30 -0
- package/dist/workflow-control-tool.js +176 -0
- package/dist/workflow-delivery-choice.d.ts +20 -0
- package/dist/workflow-delivery-choice.js +48 -0
- package/dist/workflow-editor.d.ts +93 -0
- package/dist/workflow-editor.js +363 -0
- package/dist/workflow-manager.d.ts +492 -0
- package/dist/workflow-manager.js +1124 -0
- package/dist/workflow-paths.d.ts +22 -0
- package/dist/workflow-paths.js +46 -0
- package/dist/workflow-release-gate.d.ts +39 -0
- package/dist/workflow-release-gate.js +309 -0
- package/dist/workflow-saved.d.ts +38 -0
- package/dist/workflow-saved.js +126 -0
- package/dist/workflow-settings.d.ts +70 -0
- package/dist/workflow-settings.js +131 -0
- package/dist/workflow-tool.d.ts +71 -0
- package/dist/workflow-tool.js +367 -0
- package/dist/workflow-ui.d.ts +182 -0
- package/dist/workflow-ui.js +1587 -0
- package/dist/workflow.d.ts +333 -0
- package/dist/workflow.js +1151 -0
- package/dist/workflows-models-command.d.ts +31 -0
- package/dist/workflows-models-command.js +156 -0
- package/dist/worktree.d.ts +25 -0
- package/dist/worktree.js +61 -0
- package/extensions/workflow.ts +151 -0
- package/package.json +104 -0
- package/skills/workflow-authoring/SKILL.md +30 -0
- package/skills/workflow-authoring/examples/adversarial-verification.js +63 -0
- package/skills/workflow-authoring/examples/bounded-semantic-retry.js +56 -0
- package/skills/workflow-authoring/examples/classify-and-act.js +67 -0
- package/skills/workflow-authoring/examples/fan-out-and-synthesize.js +47 -0
- package/skills/workflow-authoring/examples/generate-and-filter.js +68 -0
- package/skills/workflow-authoring/examples/loop-until-done.js +68 -0
- package/skills/workflow-authoring/examples/phased-budgets.js +75 -0
- package/skills/workflow-authoring/examples/saved-nested-workflows.js +44 -0
- package/skills/workflow-authoring/examples/structured-output.js +37 -0
- package/skills/workflow-authoring/examples/tournament.js +81 -0
- package/skills/workflow-authoring/examples/validated-gate.js +63 -0
- package/skills/workflow-authoring/references/capabilities.md +41 -0
- package/skills/workflow-authoring/references/capability-details.md +357 -0
- package/skills/workflow-authoring/references/common-helpers.md +4 -0
- package/skills/workflow-authoring/references/debugging.md +27 -0
- package/skills/workflow-authoring/references/focused-recipes.md +11 -0
- package/skills/workflow-authoring/references/helpers.md +9 -0
- package/skills/workflow-authoring/references/lifecycle.md +33 -0
- package/skills/workflow-authoring/references/pattern-selection.md +15 -0
- package/skills/workflow-authoring/references/quality-helpers.md +8 -0
- package/skills/workflow-authoring/references/registry-ownership.md +15 -0
- package/skills/workflow-authoring/references/retry-helper.md +5 -0
- package/skills/workflow-authoring/references/review.md +43 -0
- package/skills/workflow-authoring/references/runtime.md +27 -0
- package/skills/workflow-authoring/references/specialized-helpers.md +19 -0
- package/skills/workflow-authoring/references/versions.md +13 -0
- package/skills/workflow-patterns/SKILL.md +51 -0
- package/src/accept-workflow-guidance.ts +71 -0
- package/src/adversarial-review.ts +120 -0
- package/src/agent-history.ts +157 -0
- package/src/agent-registry.ts +221 -0
- package/src/agent.ts +929 -0
- package/src/builtin-commands.ts +286 -0
- package/src/builtin-workflows.ts +155 -0
- package/src/code-review.ts +183 -0
- package/src/config.ts +55 -0
- package/src/deep-research.ts +135 -0
- package/src/display.ts +367 -0
- package/src/effort-command.ts +87 -0
- package/src/enums.ts +77 -0
- package/src/errors.ts +199 -0
- package/src/extension-reload.ts +100 -0
- package/src/fs-persistence.ts +124 -0
- package/src/index.ts +176 -0
- package/src/logger.ts +88 -0
- package/src/model-routing.ts +73 -0
- package/src/model-spec.ts +309 -0
- package/src/model-tier-config.ts +296 -0
- package/src/run-persistence.ts +484 -0
- package/src/saved-commands.ts +115 -0
- package/src/shared-store.ts +228 -0
- package/src/structured-output.ts +47 -0
- package/src/task-panel.ts +490 -0
- package/src/usage-limit-scheduler.ts +432 -0
- package/src/web-tools.ts +124 -0
- package/src/workflow-authoring-coverage.ts +486 -0
- package/src/workflow-authoring-reference.ts +186 -0
- package/src/workflow-capability-contract.ts +806 -0
- package/src/workflow-commands.ts +287 -0
- package/src/workflow-comprehension.ts +1673 -0
- package/src/workflow-context-measurement.ts +262 -0
- package/src/workflow-control-tool.ts +238 -0
- package/src/workflow-delivery-choice.ts +69 -0
- package/src/workflow-editor.ts +444 -0
- package/src/workflow-manager.ts +1405 -0
- package/src/workflow-paths.ts +63 -0
- package/src/workflow-release-gate.ts +529 -0
- package/src/workflow-saved.ts +180 -0
- package/src/workflow-settings.ts +194 -0
- package/src/workflow-tool.ts +464 -0
- package/src/workflow-ui.ts +1789 -0
- package/src/workflow.ts +1615 -0
- package/src/workflows-models-command.ts +211 -0
- 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
|
+
}
|
package/dist/index.d.ts
ADDED
|
@@ -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";
|
package/dist/logger.d.ts
ADDED
|
@@ -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
|
+
}
|