@elizaos/plugin-workflow 2.0.0-beta.1 → 2.0.3-beta.5
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 +21 -0
- package/README.md +28 -26
- package/dist/actions/eval-code.d.ts +12 -0
- package/dist/actions/eval-code.d.ts.map +1 -0
- package/dist/actions/eval-code.js +59 -0
- package/dist/actions/eval-code.js.map +1 -0
- package/dist/actions/index.d.ts +1 -0
- package/dist/actions/index.d.ts.map +1 -1
- package/dist/actions/index.js +1 -0
- package/dist/actions/index.js.map +1 -1
- package/dist/actions/workflow.d.ts +7 -0
- package/dist/actions/workflow.d.ts.map +1 -1
- package/dist/actions/workflow.js +462 -10
- package/dist/actions/workflow.js.map +1 -1
- package/dist/db/schema.d.ts +196 -0
- package/dist/db/schema.d.ts.map +1 -1
- package/dist/db/schema.js +23 -0
- package/dist/db/schema.js.map +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +9 -64
- package/dist/index.js.map +1 -1
- package/dist/lib/automations-builder.d.ts.map +1 -1
- package/dist/lib/automations-builder.js +10 -35
- package/dist/lib/automations-builder.js.map +1 -1
- package/dist/lib/automations-types.d.ts +2 -2
- package/dist/lib/automations-types.d.ts.map +1 -1
- package/dist/lib/automations-types.js.map +1 -1
- package/dist/lib/index.d.ts +0 -2
- package/dist/lib/index.d.ts.map +1 -1
- package/dist/lib/index.js +1 -2
- package/dist/lib/index.js.map +1 -1
- package/dist/lib/workflow-clarification.d.ts +2 -2
- package/dist/lib/workflow-clarification.d.ts.map +1 -1
- package/dist/lib/workflow-clarification.js +15 -11
- package/dist/lib/workflow-clarification.js.map +1 -1
- package/dist/plugin-routes.d.ts.map +1 -1
- package/dist/plugin-routes.js +6 -0
- package/dist/plugin-routes.js.map +1 -1
- package/dist/providers/activeWorkflows.js +2 -2
- package/dist/providers/activeWorkflows.js.map +1 -1
- package/dist/providers/workflowStatus.js +1 -1
- package/dist/providers/workflowStatus.js.map +1 -1
- package/dist/routes/workflow-routes.d.ts.map +1 -1
- package/dist/routes/workflow-routes.js +68 -2
- package/dist/routes/workflow-routes.js.map +1 -1
- package/dist/routes/workflows.d.ts.map +1 -1
- package/dist/routes/workflows.js +5 -1
- package/dist/routes/workflows.js.map +1 -1
- package/dist/services/embedded-workflow-service.d.ts +74 -17
- package/dist/services/embedded-workflow-service.d.ts.map +1 -1
- package/dist/services/embedded-workflow-service.js +343 -149
- package/dist/services/embedded-workflow-service.js.map +1 -1
- package/dist/services/smithers-runtime.d.ts +47 -0
- package/dist/services/smithers-runtime.d.ts.map +1 -0
- package/dist/services/smithers-runtime.js +444 -0
- package/dist/services/smithers-runtime.js.map +1 -0
- package/dist/services/workflow-credential-store.js +1 -1
- package/dist/services/workflow-credential-store.js.map +1 -1
- package/dist/services/workflow-dispatch.d.ts +31 -1
- package/dist/services/workflow-dispatch.d.ts.map +1 -1
- package/dist/services/workflow-dispatch.js +75 -10
- package/dist/services/workflow-dispatch.js.map +1 -1
- package/dist/services/workflow-service.d.ts +27 -1
- package/dist/services/workflow-service.d.ts.map +1 -1
- package/dist/services/workflow-service.js +133 -11
- package/dist/services/workflow-service.js.map +1 -1
- package/dist/trigger-routes.d.ts +2 -18
- package/dist/trigger-routes.d.ts.map +1 -1
- package/dist/trigger-routes.js +11 -39
- package/dist/trigger-routes.js.map +1 -1
- package/dist/types/index.d.ts +82 -2
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/index.js.map +1 -1
- package/dist/types/workflow-contracts.d.ts +118 -0
- package/dist/types/workflow-contracts.d.ts.map +1 -0
- package/dist/types/workflow-contracts.js +2 -0
- package/dist/types/workflow-contracts.js.map +1 -0
- package/dist/utils/catalog.js +2 -2
- package/dist/utils/catalog.js.map +1 -1
- package/dist/utils/clarification.d.ts +1 -1
- package/dist/utils/clarification.d.ts.map +1 -1
- package/dist/utils/clarification.js +15 -4
- package/dist/utils/clarification.js.map +1 -1
- package/dist/utils/context.js +1 -1
- package/dist/utils/context.js.map +1 -1
- package/dist/utils/evaluation-samples.d.ts +6 -0
- package/dist/utils/evaluation-samples.d.ts.map +1 -0
- package/dist/utils/evaluation-samples.js +216 -0
- package/dist/utils/evaluation-samples.js.map +1 -0
- package/dist/utils/execution-diagnostics.d.ts +26 -0
- package/dist/utils/execution-diagnostics.d.ts.map +1 -0
- package/dist/utils/execution-diagnostics.js +159 -0
- package/dist/utils/execution-diagnostics.js.map +1 -0
- package/dist/utils/generation.d.ts.map +1 -1
- package/dist/utils/generation.js +134 -19
- package/dist/utils/generation.js.map +1 -1
- package/dist/utils/host-capabilities.d.ts.map +1 -1
- package/dist/utils/host-capabilities.js +20 -5
- package/dist/utils/host-capabilities.js.map +1 -1
- package/dist/utils/inferSyntheticOutputSchema.js +3 -3
- package/dist/utils/inferSyntheticOutputSchema.js.map +1 -1
- package/dist/utils/outputSchema.js +1 -1
- package/dist/utils/outputSchema.js.map +1 -1
- package/dist/utils/validateAndRepair.js +10 -10
- package/dist/utils/validateAndRepair.js.map +1 -1
- package/dist/utils/workflow-prompts/draftIntent.d.ts +1 -1
- package/dist/utils/workflow-prompts/draftIntent.d.ts.map +1 -1
- package/dist/utils/workflow-prompts/draftIntent.js +1 -1
- package/dist/utils/workflow-prompts/keywordExtraction.d.ts +1 -1
- package/dist/utils/workflow-prompts/keywordExtraction.d.ts.map +1 -1
- package/dist/utils/workflow-prompts/keywordExtraction.js +1 -1
- package/dist/utils/workflow-prompts/workflowGeneration.d.ts +1 -1
- package/dist/utils/workflow-prompts/workflowGeneration.d.ts.map +1 -1
- package/dist/utils/workflow-prompts/workflowGeneration.js +4 -4
- package/dist/utils/workflow-prompts/workflowMatching.d.ts +1 -1
- package/dist/utils/workflow-prompts/workflowMatching.d.ts.map +1 -1
- package/dist/utils/workflow-prompts/workflowMatching.js +1 -1
- package/dist/utils/workflow.d.ts +1 -0
- package/dist/utils/workflow.d.ts.map +1 -1
- package/dist/utils/workflow.js +44 -8
- package/dist/utils/workflow.js.map +1 -1
- package/package.json +27 -8
- package/registry-entry.json +25 -0
- package/src/actions/eval-code.ts +81 -0
- package/src/actions/index.ts +1 -0
- package/src/actions/workflow.ts +518 -10
- package/src/db/schema.ts +31 -0
- package/src/index.ts +9 -82
- package/src/lib/automations-builder.ts +11 -35
- package/src/lib/automations-types.ts +1 -2
- package/src/lib/index.ts +0 -8
- package/src/lib/workflow-clarification.ts +18 -13
- package/src/plugin-routes.ts +6 -0
- package/src/providers/activeWorkflows.ts +2 -2
- package/src/providers/workflowStatus.ts +1 -1
- package/src/routes/workflow-routes.ts +100 -2
- package/src/routes/workflows.ts +5 -1
- package/src/services/embedded-workflow-service.ts +447 -172
- package/src/services/smithers-runtime.ts +526 -0
- package/src/services/workflow-credential-store.ts +1 -1
- package/src/services/workflow-dispatch.ts +116 -13
- package/src/services/workflow-service.ts +186 -10
- package/src/trigger-routes.ts +12 -70
- package/src/types/index.ts +94 -2
- package/src/types/workflow-contracts.ts +166 -0
- package/src/utils/catalog.ts +2 -2
- package/src/utils/clarification.ts +19 -5
- package/src/utils/context.ts +1 -1
- package/src/utils/evaluation-samples.ts +239 -0
- package/src/utils/execution-diagnostics.ts +192 -0
- package/src/utils/generation.ts +224 -32
- package/src/utils/host-capabilities.ts +21 -5
- package/src/utils/inferSyntheticOutputSchema.ts +3 -3
- package/src/utils/outputSchema.ts +1 -1
- package/src/utils/validateAndRepair.ts +10 -10
- package/src/utils/workflow-prompts/draftIntent.ts +1 -1
- package/src/utils/workflow-prompts/keywordExtraction.ts +1 -1
- package/src/utils/workflow-prompts/workflowGeneration.ts +4 -4
- package/src/utils/workflow-prompts/workflowMatching.ts +1 -1
- package/src/utils/workflow.ts +56 -8
- package/dist/lib/legacy-task-migration.d.ts +0 -20
- package/dist/lib/legacy-task-migration.d.ts.map +0 -1
- package/dist/lib/legacy-task-migration.js +0 -110
- package/dist/lib/legacy-task-migration.js.map +0 -1
- package/dist/lib/legacy-text-trigger-migration.d.ts +0 -18
- package/dist/lib/legacy-text-trigger-migration.d.ts.map +0 -1
- package/dist/lib/legacy-text-trigger-migration.js +0 -131
- package/dist/lib/legacy-text-trigger-migration.js.map +0 -1
- package/src/lib/legacy-task-migration.ts +0 -143
- package/src/lib/legacy-text-trigger-migration.ts +0 -178
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
import type { WorkflowExecution } from '../types/index';
|
|
2
|
+
|
|
3
|
+
export type WorkflowExecutionTone = 'success' | 'danger' | 'warning' | 'muted';
|
|
4
|
+
|
|
5
|
+
export interface WorkflowExecutionRunRow {
|
|
6
|
+
nodeName: string;
|
|
7
|
+
status: 'success' | 'error' | 'unknown';
|
|
8
|
+
startTime?: number;
|
|
9
|
+
executionTimeMs?: number;
|
|
10
|
+
itemCount: number;
|
|
11
|
+
preview: string;
|
|
12
|
+
error?: string;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export interface WorkflowExecutionSummary {
|
|
16
|
+
statusLabel: string;
|
|
17
|
+
tone: WorkflowExecutionTone;
|
|
18
|
+
durationLabel: string;
|
|
19
|
+
nodeCount: number;
|
|
20
|
+
lastNode?: string;
|
|
21
|
+
error?: string;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function isRecord(value: unknown): value is Record<string, unknown> {
|
|
25
|
+
return typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function readNumber(value: unknown): number | undefined {
|
|
29
|
+
return typeof value === 'number' && Number.isFinite(value) ? value : undefined;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function readString(value: unknown): string | undefined {
|
|
33
|
+
return typeof value === 'string' && value.trim() ? value : undefined;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function countMainItems(data: unknown): number {
|
|
37
|
+
if (!isRecord(data) || !Array.isArray(data.main)) return 0;
|
|
38
|
+
return data.main.reduce((total, output) => {
|
|
39
|
+
if (!Array.isArray(output)) return total;
|
|
40
|
+
return total + output.length;
|
|
41
|
+
}, 0);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function previewMainData(data: unknown): string {
|
|
45
|
+
if (!isRecord(data) || !Array.isArray(data.main)) return 'No output';
|
|
46
|
+
for (const output of data.main) {
|
|
47
|
+
if (!Array.isArray(output)) continue;
|
|
48
|
+
const first = output.find(isRecord);
|
|
49
|
+
if (!first) continue;
|
|
50
|
+
const json = isRecord(first.json) ? first.json : first;
|
|
51
|
+
try {
|
|
52
|
+
const preview = JSON.stringify(json);
|
|
53
|
+
return preview.length > 160 ? `${preview.slice(0, 157)}...` : preview;
|
|
54
|
+
} catch {
|
|
55
|
+
return 'Output could not be previewed';
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
return 'No output';
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
function getRunError(run: unknown): string | undefined {
|
|
62
|
+
if (!isRecord(run)) return undefined;
|
|
63
|
+
const error = run.error;
|
|
64
|
+
if (isRecord(error)) {
|
|
65
|
+
return readString(error.message) ?? readString(error.description);
|
|
66
|
+
}
|
|
67
|
+
return readString(error);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
export function getWorkflowExecutionRunRows(
|
|
71
|
+
execution: WorkflowExecution
|
|
72
|
+
): WorkflowExecutionRunRow[] {
|
|
73
|
+
const runData = execution.data?.resultData?.runData;
|
|
74
|
+
if (!runData) return [];
|
|
75
|
+
|
|
76
|
+
return Object.entries(runData).flatMap(([nodeName, runs]) => {
|
|
77
|
+
if (!Array.isArray(runs)) return [];
|
|
78
|
+
return runs.map((run): WorkflowExecutionRunRow => {
|
|
79
|
+
const record = isRecord(run) ? run : {};
|
|
80
|
+
const error = getRunError(record);
|
|
81
|
+
const data = record.data;
|
|
82
|
+
return {
|
|
83
|
+
nodeName,
|
|
84
|
+
status: error ? 'error' : execution.status === 'success' ? 'success' : 'unknown',
|
|
85
|
+
startTime: readNumber(record.startTime),
|
|
86
|
+
executionTimeMs: readNumber(record.executionTime),
|
|
87
|
+
itemCount: countMainItems(data),
|
|
88
|
+
preview: previewMainData(data),
|
|
89
|
+
error,
|
|
90
|
+
};
|
|
91
|
+
});
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
export function getWorkflowExecutionError(execution: WorkflowExecution): string | undefined {
|
|
96
|
+
const error = execution.data?.resultData?.error;
|
|
97
|
+
if (error?.message) return error.message;
|
|
98
|
+
for (const row of getWorkflowExecutionRunRows(execution)) {
|
|
99
|
+
if (row.error) return row.error;
|
|
100
|
+
}
|
|
101
|
+
return undefined;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
export function formatWorkflowExecutionDuration(
|
|
105
|
+
startedAt?: string,
|
|
106
|
+
stoppedAt?: string | null
|
|
107
|
+
): string {
|
|
108
|
+
if (!startedAt) return 'Unknown';
|
|
109
|
+
const startMs = Date.parse(startedAt);
|
|
110
|
+
const stopMs = stoppedAt ? Date.parse(stoppedAt) : Date.now();
|
|
111
|
+
if (!Number.isFinite(startMs) || !Number.isFinite(stopMs)) return 'Unknown';
|
|
112
|
+
const durationMs = Math.max(0, stopMs - startMs);
|
|
113
|
+
if (durationMs < 1000) return `${durationMs} ms`;
|
|
114
|
+
if (durationMs < 60_000) return `${(durationMs / 1000).toFixed(1)} s`;
|
|
115
|
+
return `${Math.round(durationMs / 60_000)} min`;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
export function summarizeWorkflowExecution(execution: WorkflowExecution): WorkflowExecutionSummary {
|
|
119
|
+
const rows = getWorkflowExecutionRunRows(execution);
|
|
120
|
+
const status = execution.status;
|
|
121
|
+
const tone: WorkflowExecutionTone =
|
|
122
|
+
status === 'success'
|
|
123
|
+
? 'success'
|
|
124
|
+
: status === 'error' || status === 'crashed' || status === 'canceled'
|
|
125
|
+
? 'danger'
|
|
126
|
+
: status === 'running' || status === 'waiting'
|
|
127
|
+
? 'warning'
|
|
128
|
+
: 'muted';
|
|
129
|
+
const statusLabel =
|
|
130
|
+
status === 'success'
|
|
131
|
+
? 'Succeeded'
|
|
132
|
+
: status === 'error'
|
|
133
|
+
? 'Failed'
|
|
134
|
+
: status === 'running'
|
|
135
|
+
? 'Running'
|
|
136
|
+
: status === 'waiting'
|
|
137
|
+
? 'Waiting'
|
|
138
|
+
: status.charAt(0).toUpperCase() + status.slice(1);
|
|
139
|
+
|
|
140
|
+
return {
|
|
141
|
+
statusLabel,
|
|
142
|
+
tone,
|
|
143
|
+
durationLabel: formatWorkflowExecutionDuration(execution.startedAt, execution.stoppedAt),
|
|
144
|
+
nodeCount: rows.length,
|
|
145
|
+
lastNode: execution.data?.resultData?.lastNodeExecuted,
|
|
146
|
+
error: getWorkflowExecutionError(execution),
|
|
147
|
+
};
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
export function formatWorkflowEngineMetrics(execution: WorkflowExecution): string | null {
|
|
151
|
+
const engine = execution.data?.resultData?.engine;
|
|
152
|
+
if (!engine) return null;
|
|
153
|
+
const skipped = engine.skipped > 0 ? ` / ${engine.skipped} skipped` : '';
|
|
154
|
+
const retries = engine.retries > 0 ? ` / ${engine.retries} retries` : '';
|
|
155
|
+
return `${engine.nodes} nodes / ${engine.levels} levels / ${engine.maxConcurrency} max parallel${skipped}${retries}`;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
export function buildWorkflowExecutionDiagnostics(execution: WorkflowExecution): string {
|
|
159
|
+
const summary = summarizeWorkflowExecution(execution);
|
|
160
|
+
const rows = getWorkflowExecutionRunRows(execution);
|
|
161
|
+
const engineMetrics = formatWorkflowEngineMetrics(execution);
|
|
162
|
+
const lines = [
|
|
163
|
+
`Workflow execution ${execution.id}`,
|
|
164
|
+
`Status: ${summary.statusLabel}`,
|
|
165
|
+
`Workflow: ${execution.workflowId}`,
|
|
166
|
+
`Mode: ${execution.mode}`,
|
|
167
|
+
`Started: ${execution.startedAt}`,
|
|
168
|
+
`Stopped: ${execution.stoppedAt ?? 'still running'}`,
|
|
169
|
+
`Duration: ${summary.durationLabel}`,
|
|
170
|
+
summary.lastNode ? `Last node: ${summary.lastNode}` : null,
|
|
171
|
+
engineMetrics ? `Engine: ${engineMetrics}` : null,
|
|
172
|
+
summary.error ? `Error: ${summary.error}` : null,
|
|
173
|
+
].filter((line): line is string => Boolean(line));
|
|
174
|
+
|
|
175
|
+
if (rows.length === 0) {
|
|
176
|
+
lines.push('Nodes: none recorded');
|
|
177
|
+
return lines.join('\n');
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
lines.push('Nodes:');
|
|
181
|
+
for (const row of rows) {
|
|
182
|
+
const elapsed =
|
|
183
|
+
typeof row.executionTimeMs === 'number' ? `${row.executionTimeMs} ms` : 'unknown';
|
|
184
|
+
const result = row.error ? `error=${row.error}` : `preview=${row.preview}`;
|
|
185
|
+
lines.push(
|
|
186
|
+
`- ${row.nodeName}: ${row.status}; ${row.itemCount} item${
|
|
187
|
+
row.itemCount === 1 ? '' : 's'
|
|
188
|
+
}; ${elapsed}; ${result}`
|
|
189
|
+
);
|
|
190
|
+
}
|
|
191
|
+
return lines.join('\n');
|
|
192
|
+
}
|
package/src/utils/generation.ts
CHANGED
|
@@ -48,16 +48,179 @@ type StructuredModelRunner = {
|
|
|
48
48
|
): Promise<T>;
|
|
49
49
|
};
|
|
50
50
|
|
|
51
|
+
type WorkflowTextModelType = typeof ModelType.TEXT_SMALL | typeof ModelType.TEXT_LARGE;
|
|
52
|
+
type WorkflowGenerateTextParams = GenerateTextParams & { model?: string };
|
|
53
|
+
|
|
54
|
+
interface WorkflowModelRouting {
|
|
55
|
+
model?: string;
|
|
56
|
+
requestedProvider?: string;
|
|
57
|
+
runtimeProvider?: string;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
const WORKFLOW_MODEL_PROVIDER_KEYS = [
|
|
61
|
+
'WORKFLOW_LLM_PROVIDER',
|
|
62
|
+
'WORKFLOW_MODEL_PROVIDER',
|
|
63
|
+
'WORKFLOW_TEST_PROVIDER',
|
|
64
|
+
] as const;
|
|
65
|
+
|
|
66
|
+
const WORKFLOW_MODEL_KEYS = [
|
|
67
|
+
'WORKFLOW_LLM_MODEL',
|
|
68
|
+
'WORKFLOW_MODEL',
|
|
69
|
+
'WORKFLOW_TEST_MODEL',
|
|
70
|
+
] as const;
|
|
71
|
+
|
|
72
|
+
const WORKFLOW_RUNTIME_PROVIDER_KEYS = [
|
|
73
|
+
'WORKFLOW_LLM_RUNTIME_PROVIDER',
|
|
74
|
+
'WORKFLOW_MODEL_RUNTIME_PROVIDER',
|
|
75
|
+
] as const;
|
|
76
|
+
|
|
77
|
+
function readStringSetting(runtime: IAgentRuntime, key: string): string | undefined {
|
|
78
|
+
const runtimeValue = runtime.getSetting(key);
|
|
79
|
+
if (typeof runtimeValue === 'string' && runtimeValue.trim().length > 0) {
|
|
80
|
+
return runtimeValue.trim();
|
|
81
|
+
}
|
|
82
|
+
if (typeof process !== 'undefined') {
|
|
83
|
+
const envValue = process.env[key];
|
|
84
|
+
if (typeof envValue === 'string' && envValue.trim().length > 0) {
|
|
85
|
+
return envValue.trim();
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
return undefined;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
function readFirstStringSetting(
|
|
92
|
+
runtime: IAgentRuntime,
|
|
93
|
+
keys: readonly string[]
|
|
94
|
+
): string | undefined {
|
|
95
|
+
for (const key of keys) {
|
|
96
|
+
const value = readStringSetting(runtime, key);
|
|
97
|
+
if (value) {
|
|
98
|
+
return value;
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
return undefined;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
function isRecord(value: unknown): value is Record<string, unknown> {
|
|
105
|
+
return value !== null && typeof value === 'object' && !Array.isArray(value);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
function isCerebrasHost(value: string): boolean {
|
|
109
|
+
const host = value.toLowerCase();
|
|
110
|
+
return host === 'cerebras.ai' || host.endsWith('.cerebras.ai');
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
function isCerebrasBaseUrl(value: string): boolean {
|
|
114
|
+
try {
|
|
115
|
+
return isCerebrasHost(new URL(value).hostname);
|
|
116
|
+
} catch {
|
|
117
|
+
const host = value.replace(/^[a-z][a-z0-9+.-]*:\/\//i, '').split(/[/?#:]/, 1)[0];
|
|
118
|
+
return isCerebrasHost(host);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
function inferCerebrasMode(runtime: IAgentRuntime): boolean {
|
|
123
|
+
const explicitProvider = readStringSetting(runtime, 'ELIZA_PROVIDER');
|
|
124
|
+
if (explicitProvider?.toLowerCase() === 'cerebras') {
|
|
125
|
+
return true;
|
|
126
|
+
}
|
|
127
|
+
const openAiBaseUrl = readStringSetting(runtime, 'OPENAI_BASE_URL');
|
|
128
|
+
if (openAiBaseUrl && isCerebrasBaseUrl(openAiBaseUrl)) {
|
|
129
|
+
return true;
|
|
130
|
+
}
|
|
131
|
+
const cerebrasKey = readStringSetting(runtime, 'CEREBRAS_API_KEY');
|
|
132
|
+
return !!cerebrasKey && !readStringSetting(runtime, 'OPENAI_API_KEY') && !openAiBaseUrl;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
function resolveWorkflowModelRouting(runtime: IAgentRuntime): WorkflowModelRouting | null {
|
|
136
|
+
const explicitProvider = readFirstStringSetting(runtime, WORKFLOW_MODEL_PROVIDER_KEYS);
|
|
137
|
+
const requestedProvider =
|
|
138
|
+
explicitProvider ?? (inferCerebrasMode(runtime) ? 'cerebras' : undefined);
|
|
139
|
+
const normalizedProvider = requestedProvider?.toLowerCase();
|
|
140
|
+
const model =
|
|
141
|
+
readFirstStringSetting(runtime, WORKFLOW_MODEL_KEYS) ??
|
|
142
|
+
(normalizedProvider === 'cerebras'
|
|
143
|
+
? (readStringSetting(runtime, 'CEREBRAS_MODEL') ?? 'gpt-oss-120b')
|
|
144
|
+
: undefined);
|
|
145
|
+
const explicitRuntimeProvider = readFirstStringSetting(runtime, WORKFLOW_RUNTIME_PROVIDER_KEYS);
|
|
146
|
+
const runtimeProvider =
|
|
147
|
+
explicitRuntimeProvider ?? (normalizedProvider === 'cerebras' ? 'openai' : requestedProvider);
|
|
148
|
+
|
|
149
|
+
if (!model && !requestedProvider && !runtimeProvider) {
|
|
150
|
+
return null;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
return {
|
|
154
|
+
...(model ? { model } : {}),
|
|
155
|
+
...(requestedProvider ? { requestedProvider } : {}),
|
|
156
|
+
...(runtimeProvider ? { runtimeProvider } : {}),
|
|
157
|
+
};
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
function withWorkflowModelRouting(
|
|
161
|
+
runtime: IAgentRuntime,
|
|
162
|
+
params: GenerateTextParams,
|
|
163
|
+
callSite: string
|
|
164
|
+
): { params: WorkflowGenerateTextParams; provider?: string } {
|
|
165
|
+
const routing = resolveWorkflowModelRouting(runtime);
|
|
166
|
+
if (!routing) {
|
|
167
|
+
return { params };
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
const existingProviderOptions = isRecord(params.providerOptions) ? params.providerOptions : {};
|
|
171
|
+
const existingWorkflowOptions = isRecord(existingProviderOptions.workflow)
|
|
172
|
+
? existingProviderOptions.workflow
|
|
173
|
+
: {};
|
|
174
|
+
|
|
175
|
+
return {
|
|
176
|
+
provider: routing.runtimeProvider,
|
|
177
|
+
params: {
|
|
178
|
+
...params,
|
|
179
|
+
...(routing.model ? { model: routing.model } : {}),
|
|
180
|
+
providerOptions: {
|
|
181
|
+
...existingProviderOptions,
|
|
182
|
+
workflow: {
|
|
183
|
+
...existingWorkflowOptions,
|
|
184
|
+
callSite,
|
|
185
|
+
...(routing.model ? { model: routing.model } : {}),
|
|
186
|
+
...(routing.requestedProvider ? { requestedProvider: routing.requestedProvider } : {}),
|
|
187
|
+
...(routing.runtimeProvider ? { runtimeProvider: routing.runtimeProvider } : {}),
|
|
188
|
+
},
|
|
189
|
+
},
|
|
190
|
+
},
|
|
191
|
+
};
|
|
192
|
+
}
|
|
193
|
+
|
|
51
194
|
async function useStructuredModel<T>(
|
|
52
195
|
runtime: IAgentRuntime,
|
|
53
196
|
prompt: string,
|
|
54
|
-
schema: unknown
|
|
197
|
+
schema: unknown,
|
|
198
|
+
callSite: string
|
|
55
199
|
): Promise<T> {
|
|
56
200
|
const structuredRuntime = runtime as IAgentRuntime & StructuredModelRunner;
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
201
|
+
const routed = withWorkflowModelRouting(
|
|
202
|
+
runtime,
|
|
203
|
+
{
|
|
204
|
+
prompt,
|
|
205
|
+
responseSchema: schema as never,
|
|
206
|
+
},
|
|
207
|
+
callSite
|
|
208
|
+
);
|
|
209
|
+
return (await structuredRuntime.useModel<T>(
|
|
210
|
+
ModelType.TEXT_SMALL,
|
|
211
|
+
routed.params as GenerateTextParams & { responseSchema: unknown },
|
|
212
|
+
routed.provider
|
|
213
|
+
)) as T;
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
async function useWorkflowTextModel(
|
|
217
|
+
runtime: IAgentRuntime,
|
|
218
|
+
modelType: WorkflowTextModelType,
|
|
219
|
+
params: GenerateTextParams,
|
|
220
|
+
callSite: string
|
|
221
|
+
): Promise<string> {
|
|
222
|
+
const routed = withWorkflowModelRouting(runtime, params, callSite);
|
|
223
|
+
return (await runtime.useModel(modelType, routed.params, routed.provider)) as string;
|
|
61
224
|
}
|
|
62
225
|
|
|
63
226
|
/**
|
|
@@ -85,7 +248,8 @@ export async function extractKeywords(
|
|
|
85
248
|
result = await useStructuredModel<KeywordExtractionResult>(
|
|
86
249
|
runtime,
|
|
87
250
|
`${KEYWORD_EXTRACTION_SYSTEM_PROMPT}${buildPreferredProvidersDirective(preferredProviders)}\n\nUser request: ${userPrompt}`,
|
|
88
|
-
keywordExtractionSchema
|
|
251
|
+
keywordExtractionSchema,
|
|
252
|
+
'extractKeywords'
|
|
89
253
|
);
|
|
90
254
|
} catch (error) {
|
|
91
255
|
const errMsg = error instanceof Error ? error.message : String(error);
|
|
@@ -97,7 +261,7 @@ export async function extractKeywords(
|
|
|
97
261
|
}
|
|
98
262
|
|
|
99
263
|
// Validate structure
|
|
100
|
-
if (!result
|
|
264
|
+
if (!result || typeof result !== 'object' || !Array.isArray(result.keywords)) {
|
|
101
265
|
logger.error(
|
|
102
266
|
{
|
|
103
267
|
src: 'plugin:workflow:generation:keywords',
|
|
@@ -153,7 +317,8 @@ ${workflowList}`;
|
|
|
153
317
|
result = await useStructuredModel<WorkflowMatchResult>(
|
|
154
318
|
runtime,
|
|
155
319
|
`${WORKFLOW_MATCHING_SYSTEM_PROMPT}\n\n${userPrompt}`,
|
|
156
|
-
workflowMatchingSchema
|
|
320
|
+
workflowMatchingSchema,
|
|
321
|
+
'matchWorkflow'
|
|
157
322
|
);
|
|
158
323
|
} catch (innerError) {
|
|
159
324
|
const errMsg = innerError instanceof Error ? innerError.message : String(innerError);
|
|
@@ -218,7 +383,8 @@ ${draftSummary}
|
|
|
218
383
|
## User Message
|
|
219
384
|
|
|
220
385
|
${userMessage}`,
|
|
221
|
-
draftIntentSchema
|
|
386
|
+
draftIntentSchema,
|
|
387
|
+
'classifyDraftIntent'
|
|
222
388
|
);
|
|
223
389
|
} catch (error) {
|
|
224
390
|
const errMsg = error instanceof Error ? error.message : String(error);
|
|
@@ -233,7 +399,7 @@ ${userMessage}`,
|
|
|
233
399
|
}
|
|
234
400
|
|
|
235
401
|
const validIntents = ['confirm', 'cancel', 'modify', 'new'] as const;
|
|
236
|
-
if (!result
|
|
402
|
+
if (!result.intent || !validIntents.includes(result.intent as (typeof validIntents)[number])) {
|
|
237
403
|
logger.warn(
|
|
238
404
|
{ src: 'plugin:workflow:generation:intent' },
|
|
239
405
|
`Invalid intent from LLM: ${JSON.stringify(result)}, re-showing preview`
|
|
@@ -301,11 +467,16 @@ Return the COMPLETE corrected workflow JSON. Preserve every field that was not p
|
|
|
301
467
|
|
|
302
468
|
let response: string;
|
|
303
469
|
try {
|
|
304
|
-
response =
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
470
|
+
response = await useWorkflowTextModel(
|
|
471
|
+
runtime,
|
|
472
|
+
ModelType.TEXT_LARGE,
|
|
473
|
+
{
|
|
474
|
+
prompt: fixPrompt,
|
|
475
|
+
temperature: 0,
|
|
476
|
+
responseFormat: { type: 'json_object' },
|
|
477
|
+
},
|
|
478
|
+
'fixWorkflowErrors'
|
|
479
|
+
);
|
|
309
480
|
} catch (err) {
|
|
310
481
|
const errMsg = err instanceof Error ? err.message : String(err);
|
|
311
482
|
logger.error(
|
|
@@ -501,11 +672,16 @@ async function callLlmAndParseWorkflow(
|
|
|
501
672
|
context: 'generateWorkflow' | 'modifyWorkflow'
|
|
502
673
|
): Promise<WorkflowDefinition> {
|
|
503
674
|
const callOnce = async (extraInstruction?: string): Promise<string> =>
|
|
504
|
-
(
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
675
|
+
useWorkflowTextModel(
|
|
676
|
+
runtime,
|
|
677
|
+
ModelType.TEXT_LARGE,
|
|
678
|
+
{
|
|
679
|
+
prompt: extraInstruction ? `${prompt}\n\n${extraInstruction}` : prompt,
|
|
680
|
+
temperature: 0,
|
|
681
|
+
responseFormat: { type: 'json_object' },
|
|
682
|
+
},
|
|
683
|
+
context
|
|
684
|
+
);
|
|
509
685
|
|
|
510
686
|
const firstResponse = await callOnce();
|
|
511
687
|
try {
|
|
@@ -630,9 +806,14 @@ export async function formatActionResponse(
|
|
|
630
806
|
data: Record<string, unknown>
|
|
631
807
|
): Promise<string> {
|
|
632
808
|
try {
|
|
633
|
-
const response = await
|
|
634
|
-
|
|
635
|
-
|
|
809
|
+
const response = await useWorkflowTextModel(
|
|
810
|
+
runtime,
|
|
811
|
+
ModelType.TEXT_SMALL,
|
|
812
|
+
{
|
|
813
|
+
prompt: `${ACTION_RESPONSE_SYSTEM_PROMPT}\n\nType: ${responseType}\n\nData:\n${formatActionDataForPrompt(data)}`,
|
|
814
|
+
},
|
|
815
|
+
'formatActionResponse'
|
|
816
|
+
);
|
|
636
817
|
|
|
637
818
|
return (response as string).trim();
|
|
638
819
|
} catch (error) {
|
|
@@ -713,7 +894,8 @@ export async function assessFeasibility(
|
|
|
713
894
|
`\n\n## Removed Integrations (unavailable)\n${removedList}` +
|
|
714
895
|
`\n\n## Available Service Integrations\n${availableList}` +
|
|
715
896
|
`\n\n## Available Utility Nodes\n${utilityList}`,
|
|
716
|
-
feasibilitySchema
|
|
897
|
+
feasibilitySchema,
|
|
898
|
+
'assessFeasibility'
|
|
717
899
|
);
|
|
718
900
|
|
|
719
901
|
return result;
|
|
@@ -756,10 +938,15 @@ export async function correctFieldReferences(
|
|
|
756
938
|
ref.expression
|
|
757
939
|
).replace('{availableFields}', ref.availableFields.join('\n'));
|
|
758
940
|
|
|
759
|
-
const corrected = await
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
941
|
+
const corrected = await useWorkflowTextModel(
|
|
942
|
+
runtime,
|
|
943
|
+
ModelType.TEXT_SMALL,
|
|
944
|
+
{
|
|
945
|
+
prompt: `${FIELD_CORRECTION_SYSTEM_PROMPT}\n\n${userPrompt}`,
|
|
946
|
+
temperature: 0,
|
|
947
|
+
},
|
|
948
|
+
'correctFieldReferences'
|
|
949
|
+
);
|
|
763
950
|
|
|
764
951
|
const cleaned = (corrected as string).trim();
|
|
765
952
|
return {
|
|
@@ -915,10 +1102,15 @@ export async function correctParameterNames(
|
|
|
915
1102
|
.replace('{currentParams}', JSON.stringify(detection.currentParams, null, 2))
|
|
916
1103
|
.replace('{propertyDefs}', JSON.stringify(detection.propertyDefs, null, 2));
|
|
917
1104
|
|
|
918
|
-
const response = await
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
1105
|
+
const response = await useWorkflowTextModel(
|
|
1106
|
+
runtime,
|
|
1107
|
+
ModelType.TEXT_SMALL,
|
|
1108
|
+
{
|
|
1109
|
+
prompt: `${PARAM_CORRECTION_SYSTEM_PROMPT}\n\n${userPrompt}`,
|
|
1110
|
+
temperature: 0,
|
|
1111
|
+
},
|
|
1112
|
+
'correctParameterNames'
|
|
1113
|
+
);
|
|
922
1114
|
|
|
923
1115
|
const cleaned = (response as string)
|
|
924
1116
|
.replace(/^[\s\S]*?```(?:json)?\s*\n?/i, '')
|
|
@@ -31,7 +31,7 @@ export function detectHostCapabilities(): HostCapabilities {
|
|
|
31
31
|
// Cloudflare Workers — runtime exposes navigator.userAgent === 'Cloudflare-Workers'.
|
|
32
32
|
if (
|
|
33
33
|
typeof navigator !== 'undefined' &&
|
|
34
|
-
typeof navigator
|
|
34
|
+
typeof navigator.userAgent === 'string' &&
|
|
35
35
|
navigator.userAgent.includes('Cloudflare-Workers')
|
|
36
36
|
) {
|
|
37
37
|
return {
|
|
@@ -45,24 +45,40 @@ export function detectHostCapabilities(): HostCapabilities {
|
|
|
45
45
|
}
|
|
46
46
|
|
|
47
47
|
// Capacitor (iOS / Android). Capacitor exposes a global on the window/globalThis.
|
|
48
|
+
// `longRunning` is conditional on a registered BackgroundRunner plugin: without
|
|
49
|
+
// it, the JS context suspends within seconds of backgrounding (iOS WKWebView
|
|
50
|
+
// aggressively) and any `requiresLongRunning` node (scheduleTrigger, etc.)
|
|
51
|
+
// is dead the moment the user leaves the app. With it, the OS may wake the
|
|
52
|
+
// runner JS context periodically (≥15 min on both platforms) and the engine
|
|
53
|
+
// can argue it has cross-suspend continuity. We detect by probing for the
|
|
54
|
+
// plugin instance rather than trusting a build-time flag.
|
|
48
55
|
const capacitor: unknown = Reflect.get(globalThis, 'Capacitor');
|
|
49
56
|
if (capacitor && typeof capacitor === 'object') {
|
|
57
|
+
const plugins: unknown = Reflect.get(capacitor as object, 'Plugins');
|
|
58
|
+
const bgRunner: unknown =
|
|
59
|
+
plugins && typeof plugins === 'object'
|
|
60
|
+
? Reflect.get(plugins as object, 'BackgroundRunner')
|
|
61
|
+
: undefined;
|
|
62
|
+
const hasBgRunner = typeof bgRunner === 'object' && bgRunner !== null;
|
|
50
63
|
return {
|
|
51
64
|
fs: false,
|
|
52
65
|
inbound: false, // No public HTTP without plugin-tunnel
|
|
53
|
-
longRunning:
|
|
66
|
+
longRunning: hasBgRunner,
|
|
54
67
|
childProcess: false,
|
|
55
68
|
net: false,
|
|
56
|
-
label:
|
|
69
|
+
label: hasBgRunner
|
|
70
|
+
? 'Mobile (Capacitor + BackgroundRunner)'
|
|
71
|
+
: 'Mobile (Capacitor, foreground-only)',
|
|
57
72
|
};
|
|
58
73
|
}
|
|
59
74
|
|
|
60
|
-
// Browser without Capacitor — pure web.
|
|
75
|
+
// Browser without Capacitor — pure web. Browser tabs can be backgrounded
|
|
76
|
+
// and discarded; treat as short-lived for scheduling purposes.
|
|
61
77
|
if (typeof window !== 'undefined' && typeof process === 'undefined') {
|
|
62
78
|
return {
|
|
63
79
|
fs: false,
|
|
64
80
|
inbound: false,
|
|
65
|
-
longRunning:
|
|
81
|
+
longRunning: false,
|
|
66
82
|
childProcess: false,
|
|
67
83
|
net: false,
|
|
68
84
|
label: 'Browser',
|
|
@@ -39,7 +39,7 @@ function inferSummarizeFields(node: WorkflowNode): string[] | null {
|
|
|
39
39
|
}
|
|
40
40
|
const out: string[] = [];
|
|
41
41
|
for (const entry of fields.values) {
|
|
42
|
-
if (typeof entry
|
|
42
|
+
if (typeof entry.aggregation !== 'string' || typeof entry.field !== 'string') {
|
|
43
43
|
continue;
|
|
44
44
|
}
|
|
45
45
|
const prefix = SUMMARIZE_AGG_PREFIX[entry.aggregation];
|
|
@@ -64,7 +64,7 @@ function inferSetFields(node: WorkflowNode): string[] | null {
|
|
|
64
64
|
const modern = params.assignments as { assignments?: Array<{ name?: string }> } | undefined;
|
|
65
65
|
if (modern?.assignments && Array.isArray(modern.assignments)) {
|
|
66
66
|
const names = modern.assignments
|
|
67
|
-
.map((a) => a
|
|
67
|
+
.map((a) => a.name)
|
|
68
68
|
.filter((n): n is string => typeof n === 'string' && n.length > 0);
|
|
69
69
|
if (names.length > 0) {
|
|
70
70
|
return names;
|
|
@@ -80,7 +80,7 @@ function inferSetFields(node: WorkflowNode): string[] | null {
|
|
|
80
80
|
continue;
|
|
81
81
|
}
|
|
82
82
|
for (const v of arr) {
|
|
83
|
-
if (typeof v
|
|
83
|
+
if (typeof v.name === 'string' && v.name.length > 0) {
|
|
84
84
|
names.push(v.name);
|
|
85
85
|
}
|
|
86
86
|
}
|
|
@@ -93,7 +93,7 @@ export function loadTriggerOutputSchema(
|
|
|
93
93
|
return null;
|
|
94
94
|
}
|
|
95
95
|
const entry = TRIGGER_SCHEMAS[nodeType];
|
|
96
|
-
if (!entry?.outputSchema
|
|
96
|
+
if (!entry?.outputSchema.properties || Object.keys(entry.outputSchema.properties).length === 0) {
|
|
97
97
|
return null;
|
|
98
98
|
}
|
|
99
99
|
return {
|