@elizaos/plugin-workflow 2.0.0-beta.1 → 2.0.3-beta.6
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
package/src/db/schema.ts
CHANGED
|
@@ -45,6 +45,30 @@ export const embeddedWorkflows = workflowSchema.table(
|
|
|
45
45
|
})
|
|
46
46
|
);
|
|
47
47
|
|
|
48
|
+
export const workflowRevisions = workflowSchema.table(
|
|
49
|
+
'workflow_revisions',
|
|
50
|
+
{
|
|
51
|
+
id: text('id').primaryKey(),
|
|
52
|
+
workflowId: text('workflow_id').notNull(),
|
|
53
|
+
versionId: text('version_id').notNull(),
|
|
54
|
+
name: text('name').notNull(),
|
|
55
|
+
active: boolean('active').default(false).notNull(),
|
|
56
|
+
workflow: jsonb('workflow').$type<WorkflowDefinition>().notNull(),
|
|
57
|
+
createdAt: text('created_at').notNull(),
|
|
58
|
+
updatedAt: text('updated_at').notNull(),
|
|
59
|
+
capturedAt: text('captured_at').notNull(),
|
|
60
|
+
operation: text('operation').notNull(),
|
|
61
|
+
},
|
|
62
|
+
(table) => ({
|
|
63
|
+
workflowIdx: index('idx_workflow_revisions_workflow_id').on(table.workflowId),
|
|
64
|
+
versionIdx: uniqueIndex('idx_workflow_revisions_workflow_version').on(
|
|
65
|
+
table.workflowId,
|
|
66
|
+
table.versionId
|
|
67
|
+
),
|
|
68
|
+
capturedAtIdx: index('idx_workflow_revisions_captured_at').on(table.capturedAt),
|
|
69
|
+
})
|
|
70
|
+
);
|
|
71
|
+
|
|
48
72
|
export const embeddedExecutions = workflowSchema.table(
|
|
49
73
|
'embedded_executions',
|
|
50
74
|
{
|
|
@@ -56,11 +80,18 @@ export const embeddedExecutions = workflowSchema.table(
|
|
|
56
80
|
startedAt: text('started_at').notNull(),
|
|
57
81
|
stoppedAt: text('stopped_at'),
|
|
58
82
|
execution: jsonb('execution').$type<WorkflowExecution>().notNull(),
|
|
83
|
+
/**
|
|
84
|
+
* Per-dispatch idempotency key. Scheduled dispatches use
|
|
85
|
+
* `${workflowId}:${minuteBucket}` so re-arms inside the same minute
|
|
86
|
+
* collapse to a single execution. Null for ad-hoc / manual runs.
|
|
87
|
+
*/
|
|
88
|
+
idempotencyKey: text('idempotency_key'),
|
|
59
89
|
},
|
|
60
90
|
(table) => ({
|
|
61
91
|
workflowIdx: index('idx_embedded_executions_workflow_id').on(table.workflowId),
|
|
62
92
|
statusIdx: index('idx_embedded_executions_status').on(table.status),
|
|
63
93
|
startedAtIdx: index('idx_embedded_executions_started_at').on(table.startedAt),
|
|
94
|
+
idempotencyKeyIdx: index('idx_embedded_executions_idempotency_key').on(table.idempotencyKey),
|
|
64
95
|
})
|
|
65
96
|
);
|
|
66
97
|
|
package/src/index.ts
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { type IAgentRuntime, logger, type Plugin } from '@elizaos/core';
|
|
2
|
-
import { workflowAction } from './actions/index';
|
|
2
|
+
import { evalCodeAction, workflowAction } from './actions/index';
|
|
3
3
|
import * as dbSchema from './db/index';
|
|
4
|
-
import { migrateLegacyTextTriggers, migrateLegacyWorkbenchTasks } from './lib/index';
|
|
5
4
|
import {
|
|
6
5
|
activeWorkflowsProvider,
|
|
7
6
|
pendingDraftProvider,
|
|
@@ -11,7 +10,6 @@ import { workflowRoutes } from './routes/index';
|
|
|
11
10
|
import {
|
|
12
11
|
EmbeddedWorkflowService,
|
|
13
12
|
registerWorkflowDispatchService,
|
|
14
|
-
WORKFLOW_SERVICE_TYPE,
|
|
15
13
|
WorkflowCredentialStore,
|
|
16
14
|
WorkflowService,
|
|
17
15
|
} from './services/index';
|
|
@@ -55,9 +53,15 @@ export const workflowPlugin: Plugin = {
|
|
|
55
53
|
|
|
56
54
|
services: [EmbeddedWorkflowService, WorkflowService, WorkflowCredentialStore],
|
|
57
55
|
|
|
56
|
+
async dispose(runtime: IAgentRuntime) {
|
|
57
|
+
await runtime.getService<WorkflowService>(WorkflowService.serviceType)?.stop();
|
|
58
|
+
await runtime.getService<EmbeddedWorkflowService>(EmbeddedWorkflowService.serviceType)?.stop();
|
|
59
|
+
await runtime.getService<WorkflowCredentialStore>(WorkflowCredentialStore.serviceType)?.stop();
|
|
60
|
+
},
|
|
61
|
+
|
|
58
62
|
schema: dbSchema,
|
|
59
63
|
|
|
60
|
-
actions: [workflowAction],
|
|
64
|
+
actions: [workflowAction, evalCodeAction],
|
|
61
65
|
|
|
62
66
|
providers: [workflowStatusProvider, activeWorkflowsProvider, pendingDraftProvider],
|
|
63
67
|
|
|
@@ -66,7 +70,7 @@ export const workflowPlugin: Plugin = {
|
|
|
66
70
|
init: async (_config: Record<string, string>, runtime: IAgentRuntime): Promise<void> => {
|
|
67
71
|
// Check for pre-configured credentials (optional)
|
|
68
72
|
// Note: runtime.getSetting() only returns primitives — nested objects must be read directly
|
|
69
|
-
const workflowSettings = runtime.character
|
|
73
|
+
const workflowSettings = runtime.character.settings?.workflows as
|
|
70
74
|
| { credentials?: Record<string, string> }
|
|
71
75
|
| undefined;
|
|
72
76
|
if (workflowSettings?.credentials) {
|
|
@@ -83,13 +87,6 @@ export const workflowPlugin: Plugin = {
|
|
|
83
87
|
// runtime.getService("WORKFLOW_DISPATCH").execute(workflowId).
|
|
84
88
|
registerWorkflowDispatchService(runtime);
|
|
85
89
|
|
|
86
|
-
// Schedule one-shot legacy migrations off the init critical path. Service
|
|
87
|
-
// start-order is not guaranteed at init: WorkflowService may not be in
|
|
88
|
-
// the registry yet. Poll up to 10 times (1s spacing) and bail quietly
|
|
89
|
-
// if it never appears. Each migration is idempotent so a duplicate run
|
|
90
|
-
// on a future boot is harmless.
|
|
91
|
-
scheduleLegacyMigrations(runtime);
|
|
92
|
-
|
|
93
90
|
logger.info(
|
|
94
91
|
{ src: 'plugin:workflow:plugin:init' },
|
|
95
92
|
'Workflow Plugin initialized successfully (in-process runtime)'
|
|
@@ -97,76 +94,6 @@ export const workflowPlugin: Plugin = {
|
|
|
97
94
|
},
|
|
98
95
|
};
|
|
99
96
|
|
|
100
|
-
const MIGRATION_RETRY_LIMIT = 10;
|
|
101
|
-
const MIGRATION_RETRY_INTERVAL_MS = 1000;
|
|
102
|
-
|
|
103
|
-
function scheduleLegacyMigrations(runtime: IAgentRuntime): void {
|
|
104
|
-
let attempts = 0;
|
|
105
|
-
const tick = (): void => {
|
|
106
|
-
attempts += 1;
|
|
107
|
-
const ready = runtime.getService(WORKFLOW_SERVICE_TYPE);
|
|
108
|
-
if (!ready) {
|
|
109
|
-
if (attempts >= MIGRATION_RETRY_LIMIT) {
|
|
110
|
-
logger.warn(
|
|
111
|
-
{ src: 'plugin:workflow:plugin:migration' },
|
|
112
|
-
`WorkflowService still not registered after ${MIGRATION_RETRY_LIMIT} retries; legacy migrations will run on next boot`
|
|
113
|
-
);
|
|
114
|
-
return;
|
|
115
|
-
}
|
|
116
|
-
setTimeout(tick, MIGRATION_RETRY_INTERVAL_MS);
|
|
117
|
-
return;
|
|
118
|
-
}
|
|
119
|
-
void runLegacyMigrations(runtime);
|
|
120
|
-
};
|
|
121
|
-
// Defer the first attempt off the init stack so the host runtime can
|
|
122
|
-
// finish wiring before we probe the service registry.
|
|
123
|
-
setImmediate(tick);
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
async function runLegacyMigrations(runtime: IAgentRuntime): Promise<void> {
|
|
127
|
-
try {
|
|
128
|
-
const summary = await migrateLegacyWorkbenchTasks(runtime);
|
|
129
|
-
logger.info(
|
|
130
|
-
{
|
|
131
|
-
src: 'plugin:workflow:plugin:migration',
|
|
132
|
-
migrated: summary.migrated,
|
|
133
|
-
skipped: summary.skipped,
|
|
134
|
-
failed: summary.failed,
|
|
135
|
-
},
|
|
136
|
-
`Workbench-task migration: ${summary.migrated} migrated, ${summary.skipped} skipped, ${summary.failed} failed`
|
|
137
|
-
);
|
|
138
|
-
} catch (err) {
|
|
139
|
-
logger.warn(
|
|
140
|
-
{
|
|
141
|
-
src: 'plugin:workflow:plugin:migration',
|
|
142
|
-
err: err instanceof Error ? err.message : String(err),
|
|
143
|
-
},
|
|
144
|
-
'Workbench-task migration threw; continuing'
|
|
145
|
-
);
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
try {
|
|
149
|
-
const summary = await migrateLegacyTextTriggers(runtime);
|
|
150
|
-
logger.info(
|
|
151
|
-
{
|
|
152
|
-
src: 'plugin:workflow:plugin:migration',
|
|
153
|
-
migrated: summary.migrated,
|
|
154
|
-
skipped: summary.skipped,
|
|
155
|
-
failed: summary.failed,
|
|
156
|
-
},
|
|
157
|
-
`Text-trigger migration: ${summary.migrated} migrated, ${summary.skipped} skipped, ${summary.failed} failed`
|
|
158
|
-
);
|
|
159
|
-
} catch (err) {
|
|
160
|
-
logger.warn(
|
|
161
|
-
{
|
|
162
|
-
src: 'plugin:workflow:plugin:migration',
|
|
163
|
-
err: err instanceof Error ? err.message : String(err),
|
|
164
|
-
},
|
|
165
|
-
'Text-trigger migration threw; continuing'
|
|
166
|
-
);
|
|
167
|
-
}
|
|
168
|
-
}
|
|
169
|
-
|
|
170
97
|
export default workflowPlugin;
|
|
171
98
|
|
|
172
99
|
export * from './plugin-routes.js';
|
|
@@ -90,35 +90,17 @@ function normalizeDateValue(value: unknown): string | null {
|
|
|
90
90
|
}
|
|
91
91
|
|
|
92
92
|
function resolveAgentName(runtime: AgentRuntime): string {
|
|
93
|
-
return runtime.character
|
|
93
|
+
return runtime.character.name?.trim() || 'Eliza';
|
|
94
94
|
}
|
|
95
95
|
|
|
96
96
|
function isSystemTask(task: WorkbenchTaskView): boolean {
|
|
97
97
|
if (SYSTEM_TASK_NAMES.has(task.name)) {
|
|
98
98
|
return true;
|
|
99
99
|
}
|
|
100
|
-
const tags = new Set(task.tags
|
|
100
|
+
const tags = new Set(task.tags);
|
|
101
101
|
return tags.has('queue') && tags.has('repeat');
|
|
102
102
|
}
|
|
103
103
|
|
|
104
|
-
/**
|
|
105
|
-
* True when the raw runtime task carries the `migratedToWorkflowId` metadata
|
|
106
|
-
* flag set by `migrateLegacyWorkbenchTasks`. The workflow representation is
|
|
107
|
-
* already surfaced by the workflow listing path; the original task should not
|
|
108
|
-
* also appear as a coordinator-text item.
|
|
109
|
-
*/
|
|
110
|
-
function taskHasMigrationFlag(rawTasks: Task[], taskId: string): boolean {
|
|
111
|
-
for (const raw of rawTasks) {
|
|
112
|
-
if (raw.id !== taskId) continue;
|
|
113
|
-
const meta = isRecord(raw.metadata) ? raw.metadata : null;
|
|
114
|
-
if (meta && typeof meta.migratedToWorkflowId === 'string') {
|
|
115
|
-
return true;
|
|
116
|
-
}
|
|
117
|
-
return false;
|
|
118
|
-
}
|
|
119
|
-
return false;
|
|
120
|
-
}
|
|
121
|
-
|
|
122
104
|
function choosePreferredSystemTask(
|
|
123
105
|
current: WorkbenchTaskView,
|
|
124
106
|
candidate: WorkbenchTaskView
|
|
@@ -263,8 +245,7 @@ async function listTriggerTasks(runtime: AgentRuntime): Promise<Task[]> {
|
|
|
263
245
|
|
|
264
246
|
const merged = new Map<string, Task>();
|
|
265
247
|
for (const task of [...triggerTasks, ...heartbeatTasks]) {
|
|
266
|
-
const key =
|
|
267
|
-
task.id ?? `${task.name ?? ''}:${task.description ?? ''}:${(task.tags ?? []).join(',')}`;
|
|
248
|
+
const key = task.id ?? `${task.name}:${task.description ?? ''}:${(task.tags ?? []).join(',')}`;
|
|
268
249
|
if (!merged.has(key)) {
|
|
269
250
|
merged.set(key, task);
|
|
270
251
|
}
|
|
@@ -296,7 +277,7 @@ function buildCoordinatorTaskItem(
|
|
|
296
277
|
};
|
|
297
278
|
}
|
|
298
279
|
|
|
299
|
-
function
|
|
280
|
+
function _buildCoordinatorTriggerItem(
|
|
300
281
|
trigger: TriggerSummary,
|
|
301
282
|
room: AutomationRoomRecord | undefined
|
|
302
283
|
): AutomationItem {
|
|
@@ -455,7 +436,7 @@ function normalizeLastExecution(raw: WorkflowExecution): AutomationLastExecution
|
|
|
455
436
|
}
|
|
456
437
|
|
|
457
438
|
function getWorkflowService(runtime: AgentRuntime): WorkflowService | null {
|
|
458
|
-
const candidate = runtime.getService
|
|
439
|
+
const candidate = runtime.getService(WORKFLOW_SERVICE_TYPE);
|
|
459
440
|
return (candidate as WorkflowService | null) ?? null;
|
|
460
441
|
}
|
|
461
442
|
|
|
@@ -524,7 +505,7 @@ export async function buildAutomationListResponse(
|
|
|
524
505
|
.filter((room) => room.metadata.taskId)
|
|
525
506
|
.map((room) => [room.metadata.taskId as string, room])
|
|
526
507
|
);
|
|
527
|
-
const
|
|
508
|
+
const _triggerRooms = new Map(
|
|
528
509
|
rooms
|
|
529
510
|
.filter((room) => room.metadata.triggerId)
|
|
530
511
|
.map((room) => [room.metadata.triggerId as string, room])
|
|
@@ -548,11 +529,6 @@ export async function buildAutomationListResponse(
|
|
|
548
529
|
allTasks
|
|
549
530
|
.map((task) => toWorkbenchTaskView(task))
|
|
550
531
|
.filter((task): task is WorkbenchTaskView => task !== null)
|
|
551
|
-
// Tasks migrated to workflows by plugin-workflow's boot migration carry
|
|
552
|
-
// a `migratedToWorkflowId` metadata flag; their workflow representation
|
|
553
|
-
// is already in the workflowItemsById map below, so skip them here to
|
|
554
|
-
// avoid duplicate Automations entries.
|
|
555
|
-
.filter((task) => !taskHasMigrationFlag(allTasks, task.id))
|
|
556
532
|
);
|
|
557
533
|
|
|
558
534
|
const triggerTaskRecords = await listTriggerTasks(runtime);
|
|
@@ -608,7 +584,7 @@ export async function buildAutomationListResponse(
|
|
|
608
584
|
// current workflow list is an ORPHAN: the workflow was deleted but the chat
|
|
609
585
|
// room/conversation wasn't cleaned up. Surfacing those creates ghost
|
|
610
586
|
// rows the user can't dismiss. Skip them; the UI's deleteWorkflow path
|
|
611
|
-
// also deletes the conversation
|
|
587
|
+
// also deletes the conversation, so later deletions won't leak rooms.
|
|
612
588
|
const workflowOffline = workflowFetchError !== null;
|
|
613
589
|
if (workflowOffline) {
|
|
614
590
|
for (const [workflowId, room] of workflowRooms.entries()) {
|
|
@@ -642,15 +618,15 @@ export async function buildAutomationListResponse(
|
|
|
642
618
|
);
|
|
643
619
|
}
|
|
644
620
|
|
|
645
|
-
const coordinatorTriggerItems = triggerItems
|
|
646
|
-
|
|
647
|
-
|
|
621
|
+
const coordinatorTriggerItems = triggerItems.map((trigger) =>
|
|
622
|
+
_buildCoordinatorTriggerItem(trigger, _triggerRooms.get(trigger.id))
|
|
623
|
+
);
|
|
648
624
|
|
|
649
625
|
const automations = [
|
|
650
626
|
...automationDraftItems,
|
|
651
627
|
...workflowDraftItems,
|
|
652
|
-
...taskItems,
|
|
653
628
|
...coordinatorTriggerItems,
|
|
629
|
+
...taskItems,
|
|
654
630
|
...workflowItemsById.values(),
|
|
655
631
|
].sort(compareAutomationItems);
|
|
656
632
|
|
|
@@ -25,7 +25,6 @@ export type ConversationScope =
|
|
|
25
25
|
| 'page-connectors'
|
|
26
26
|
| 'page-phone'
|
|
27
27
|
| 'page-plugins'
|
|
28
|
-
| 'page-lifeops'
|
|
29
28
|
| 'page-settings'
|
|
30
29
|
| 'page-wallet'
|
|
31
30
|
| 'page-browser'
|
|
@@ -64,7 +63,7 @@ export function isAutomationConversationMetadata(
|
|
|
64
63
|
export type TriggerType = 'interval' | 'once' | 'cron' | 'event';
|
|
65
64
|
export type TriggerWakeMode = 'inject_now' | 'next_autonomy_cycle';
|
|
66
65
|
export type TriggerLastStatus = 'success' | 'error' | 'skipped';
|
|
67
|
-
export type TriggerKind = '
|
|
66
|
+
export type TriggerKind = 'workflow';
|
|
68
67
|
|
|
69
68
|
export interface TriggerSummary {
|
|
70
69
|
id: string;
|
package/src/lib/index.ts
CHANGED
|
@@ -41,6 +41,10 @@ type RawStructuredClarification = Partial<WorkflowClarificationRequest> & {
|
|
|
41
41
|
question: string;
|
|
42
42
|
};
|
|
43
43
|
|
|
44
|
+
function isRecord(value: unknown): value is Record<string, unknown> {
|
|
45
|
+
return typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
46
|
+
}
|
|
47
|
+
|
|
44
48
|
const VALID_KINDS: ReadonlySet<WorkflowClarificationRequest['kind']> = new Set([
|
|
45
49
|
'target_channel',
|
|
46
50
|
'target_server',
|
|
@@ -330,24 +334,25 @@ function appendUserNote(draft: Record<string, unknown>, value: string): void {
|
|
|
330
334
|
|
|
331
335
|
export function applyResolutions(
|
|
332
336
|
draft: Record<string, unknown>,
|
|
333
|
-
resolutions: ReadonlyArray<
|
|
337
|
+
resolutions: ReadonlyArray<unknown>
|
|
334
338
|
): { ok: true } | { ok: false; error: string; paramPath?: string } {
|
|
335
339
|
for (const r of resolutions) {
|
|
336
|
-
if (!r || typeof r.paramPath !== 'string') {
|
|
340
|
+
if (!isRecord(r) || typeof r.paramPath !== 'string') {
|
|
337
341
|
return { ok: false, error: 'resolution missing paramPath' };
|
|
338
342
|
}
|
|
339
|
-
|
|
343
|
+
const { paramPath, value } = r;
|
|
344
|
+
if (typeof value !== 'string') {
|
|
340
345
|
return {
|
|
341
346
|
ok: false,
|
|
342
347
|
error: 'resolution value must be a string',
|
|
343
|
-
paramPath
|
|
348
|
+
paramPath,
|
|
344
349
|
};
|
|
345
350
|
}
|
|
346
|
-
if (
|
|
351
|
+
if (paramPath.length === 0) {
|
|
347
352
|
// Free-form clarification with no field to wire into. Record the user's
|
|
348
353
|
// answer under draft._meta.userNotes so subsequent LLM iterations can
|
|
349
354
|
// consume the context, but don't mutate the workflow itself.
|
|
350
|
-
appendUserNote(draft,
|
|
355
|
+
appendUserNote(draft, value);
|
|
351
356
|
continue;
|
|
352
357
|
}
|
|
353
358
|
// Surface structural parse errors (unterminated bracket, empty
|
|
@@ -355,18 +360,18 @@ export function applyResolutions(
|
|
|
355
360
|
// malformed LLM emission and cannot be silently recovered into
|
|
356
361
|
// userNotes without losing the failure mode in the metrics pipeline.
|
|
357
362
|
try {
|
|
358
|
-
parseParamPath(
|
|
363
|
+
parseParamPath(paramPath);
|
|
359
364
|
} catch (err) {
|
|
360
365
|
return {
|
|
361
366
|
ok: false,
|
|
362
367
|
error: `paramPath is structurally invalid: ${
|
|
363
368
|
err instanceof Error ? err.message : String(err)
|
|
364
369
|
}`,
|
|
365
|
-
paramPath
|
|
370
|
+
paramPath,
|
|
366
371
|
};
|
|
367
372
|
}
|
|
368
373
|
try {
|
|
369
|
-
setByDotPath(draft,
|
|
374
|
+
setByDotPath(draft, paramPath, value);
|
|
370
375
|
} catch (err) {
|
|
371
376
|
// Lookup-time failure: the path parsed cleanly but didn't resolve
|
|
372
377
|
// against the current draft (e.g. references a node the LLM didn't
|
|
@@ -380,11 +385,11 @@ export function applyResolutions(
|
|
|
380
385
|
{
|
|
381
386
|
src: 'plugin:workflow:clarification:applyResolutions',
|
|
382
387
|
err: errMsg,
|
|
383
|
-
paramPath
|
|
388
|
+
paramPath,
|
|
384
389
|
},
|
|
385
|
-
`setByDotPath failed for paramPath "${
|
|
390
|
+
`setByDotPath failed for paramPath "${paramPath}"; recording "${value}" as a free-form note instead`
|
|
386
391
|
);
|
|
387
|
-
appendUserNote(draft,
|
|
392
|
+
appendUserNote(draft, value);
|
|
388
393
|
}
|
|
389
394
|
}
|
|
390
395
|
return { ok: true };
|
|
@@ -452,7 +457,7 @@ export function pruneResolvedClarifications(
|
|
|
452
457
|
|
|
453
458
|
/**
|
|
454
459
|
* Subset of `ElizaConnectorTargetCatalog` used by the route. Declared here
|
|
455
|
-
* (vs. imported from the service) so route tests can
|
|
460
|
+
* (vs. imported from the service) so route tests can provide it without
|
|
456
461
|
* spinning up the full service.
|
|
457
462
|
*/
|
|
458
463
|
export interface CatalogLike {
|
package/src/plugin-routes.ts
CHANGED
|
@@ -145,6 +145,12 @@ const workflowRouteList: Route[] = [
|
|
|
145
145
|
rawPath: true,
|
|
146
146
|
handler: workflowHandler,
|
|
147
147
|
},
|
|
148
|
+
{
|
|
149
|
+
type: 'GET',
|
|
150
|
+
path: '/api/workflow/workflows/:id/evaluation-samples',
|
|
151
|
+
rawPath: true,
|
|
152
|
+
handler: workflowHandler,
|
|
153
|
+
},
|
|
148
154
|
// Cross-cutting `/api/automations` surface — combines workflows, triggers,
|
|
149
155
|
// workbench tasks, and draft conversations into a single list view.
|
|
150
156
|
{
|
|
@@ -44,7 +44,7 @@ export const activeWorkflowsProvider: Provider = {
|
|
|
44
44
|
.slice(0, 20)
|
|
45
45
|
.map((wf) => {
|
|
46
46
|
const status = wf.active ? 'ACTIVE' : 'INACTIVE';
|
|
47
|
-
const nodeCount = wf.nodes
|
|
47
|
+
const nodeCount = wf.nodes.length || 0;
|
|
48
48
|
return `- **${wf.name}** (ID: ${wf.id}, Status: ${status}, Nodes: ${nodeCount})`;
|
|
49
49
|
})
|
|
50
50
|
.join('\n');
|
|
@@ -58,7 +58,7 @@ export const activeWorkflowsProvider: Provider = {
|
|
|
58
58
|
id: wf.id,
|
|
59
59
|
name: wf.name,
|
|
60
60
|
active: wf.active || false,
|
|
61
|
-
nodeCount: wf.nodes
|
|
61
|
+
nodeCount: wf.nodes.length || 0,
|
|
62
62
|
})),
|
|
63
63
|
},
|
|
64
64
|
values: {
|
|
@@ -42,7 +42,7 @@ export const workflowStatusProvider: Provider = {
|
|
|
42
42
|
for (const workflow of workflows.slice(0, 10)) {
|
|
43
43
|
const statusEmoji = workflow.active ? '✅' : '⏸️';
|
|
44
44
|
status += `${statusEmoji} ${workflow.name} (ID: ${workflow.id})\n`;
|
|
45
|
-
status += ` Nodes: ${workflow.nodes
|
|
45
|
+
status += ` Nodes: ${workflow.nodes.length || 0}\n`;
|
|
46
46
|
|
|
47
47
|
// Try to get last execution (if possible)
|
|
48
48
|
try {
|
|
@@ -73,10 +73,13 @@ function getWorkflowService(ctx: WorkflowRouteContext): WorkflowService | null {
|
|
|
73
73
|
return (ctx.runtime?.getService?.(WORKFLOW_SERVICE_TYPE) as WorkflowService | null) ?? null;
|
|
74
74
|
}
|
|
75
75
|
|
|
76
|
+
function isCatalogLike(value: unknown): value is CatalogLike {
|
|
77
|
+
return isRecord(value) && typeof value.listGroups === 'function';
|
|
78
|
+
}
|
|
79
|
+
|
|
76
80
|
function getConnectorTargetCatalog(ctx: WorkflowRouteContext): CatalogLike | null {
|
|
77
81
|
const raw = ctx.runtime?.getService?.('connector_target_catalog');
|
|
78
|
-
|
|
79
|
-
return candidate && typeof candidate.listGroups === 'function' ? candidate : null;
|
|
82
|
+
return isCatalogLike(raw) ? raw : null;
|
|
80
83
|
}
|
|
81
84
|
|
|
82
85
|
function isRecord(value: unknown): value is Record<string, unknown> {
|
|
@@ -247,6 +250,33 @@ async function handleGet(
|
|
|
247
250
|
sendJson(ctx, 200, await service.getWorkflow(id));
|
|
248
251
|
}
|
|
249
252
|
|
|
253
|
+
async function handleListRevisions(
|
|
254
|
+
ctx: WorkflowRouteContext,
|
|
255
|
+
service: WorkflowService,
|
|
256
|
+
id: string
|
|
257
|
+
): Promise<void> {
|
|
258
|
+
const url = new URL(`http://x${ctx.req.url ?? ''}`);
|
|
259
|
+
const rawLimit = url.searchParams.get('limit');
|
|
260
|
+
const limit = Math.min(Math.max(1, Number(rawLimit) || 20), 50);
|
|
261
|
+
const [workflow, revisions] = await Promise.all([
|
|
262
|
+
service.getWorkflow(id),
|
|
263
|
+
service.listWorkflowRevisions(id, limit),
|
|
264
|
+
]);
|
|
265
|
+
sendJson(ctx, 200, {
|
|
266
|
+
currentVersionId: workflow.versionId,
|
|
267
|
+
revisions,
|
|
268
|
+
});
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
async function handleRestoreRevision(
|
|
272
|
+
ctx: WorkflowRouteContext,
|
|
273
|
+
service: WorkflowService,
|
|
274
|
+
id: string,
|
|
275
|
+
versionId: string
|
|
276
|
+
): Promise<void> {
|
|
277
|
+
sendJson(ctx, 200, await service.restoreWorkflowRevision(id, versionId));
|
|
278
|
+
}
|
|
279
|
+
|
|
250
280
|
async function handleGenerate(ctx: WorkflowRouteContext, service: WorkflowService): Promise<void> {
|
|
251
281
|
const body = await readJsonBody(ctx.req, ctx.res);
|
|
252
282
|
if (!isRecord(body)) {
|
|
@@ -392,6 +422,37 @@ async function handleListExecutions(
|
|
|
392
422
|
sendJson(ctx, 200, { executions: response.data });
|
|
393
423
|
}
|
|
394
424
|
|
|
425
|
+
async function handleEvaluationSamples(
|
|
426
|
+
ctx: WorkflowRouteContext,
|
|
427
|
+
service: WorkflowService,
|
|
428
|
+
id: string
|
|
429
|
+
): Promise<void> {
|
|
430
|
+
const url = new URL(`http://x${ctx.req.url ?? ''}`);
|
|
431
|
+
const rawLimit = url.searchParams.get('limit');
|
|
432
|
+
const limit = Math.min(Math.max(1, Number(rawLimit) || 10), 50);
|
|
433
|
+
sendJson(ctx, 200, await service.getWorkflowEvaluationSuite(id, limit));
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
async function handleRunWorkflow(
|
|
437
|
+
ctx: WorkflowRouteContext,
|
|
438
|
+
service: WorkflowService,
|
|
439
|
+
id: string
|
|
440
|
+
): Promise<void> {
|
|
441
|
+
const execution = await service.runWorkflow(id, {
|
|
442
|
+
mode: 'manual',
|
|
443
|
+
throwOnError: false,
|
|
444
|
+
});
|
|
445
|
+
sendJson(ctx, 200, { execution });
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
async function handleGetExecution(
|
|
449
|
+
ctx: WorkflowRouteContext,
|
|
450
|
+
service: WorkflowService,
|
|
451
|
+
id: string
|
|
452
|
+
): Promise<void> {
|
|
453
|
+
sendJson(ctx, 200, { execution: await service.getExecutionDetail(id) });
|
|
454
|
+
}
|
|
455
|
+
|
|
395
456
|
export async function handleWorkflowRoutes(ctx: WorkflowRouteContext): Promise<void> {
|
|
396
457
|
const path = normalizePath(ctx.pathname);
|
|
397
458
|
const method = ctx.method.toUpperCase();
|
|
@@ -433,11 +494,36 @@ export async function handleWorkflowRoutes(ctx: WorkflowRouteContext): Promise<v
|
|
|
433
494
|
return;
|
|
434
495
|
}
|
|
435
496
|
|
|
497
|
+
if (method === 'GET' && path.startsWith('/executions/')) {
|
|
498
|
+
const executionId = decodeURIComponent(path.slice('/executions/'.length));
|
|
499
|
+
if (executionId) {
|
|
500
|
+
await handleGetExecution(ctx, service, executionId);
|
|
501
|
+
return;
|
|
502
|
+
}
|
|
503
|
+
}
|
|
504
|
+
|
|
436
505
|
const id = readId(path);
|
|
437
506
|
if (id && method === 'GET' && path === `/workflows/${encodeURIComponent(id)}`) {
|
|
438
507
|
await handleGet(ctx, service, id);
|
|
439
508
|
return;
|
|
440
509
|
}
|
|
510
|
+
if (id && method === 'GET' && path === `/workflows/${encodeURIComponent(id)}/revisions`) {
|
|
511
|
+
await handleListRevisions(ctx, service, id);
|
|
512
|
+
return;
|
|
513
|
+
}
|
|
514
|
+
if (
|
|
515
|
+
id &&
|
|
516
|
+
method === 'POST' &&
|
|
517
|
+
path.startsWith(`/workflows/${encodeURIComponent(id)}/revisions/`)
|
|
518
|
+
) {
|
|
519
|
+
const prefix = `/workflows/${encodeURIComponent(id)}/revisions/`;
|
|
520
|
+
const suffix = path.slice(prefix.length);
|
|
521
|
+
const versionId = suffix.endsWith('/restore') ? suffix.slice(0, -'/restore'.length) : '';
|
|
522
|
+
if (versionId) {
|
|
523
|
+
await handleRestoreRevision(ctx, service, id, decodeURIComponent(versionId));
|
|
524
|
+
return;
|
|
525
|
+
}
|
|
526
|
+
}
|
|
441
527
|
if (id && method === 'PUT' && path === `/workflows/${encodeURIComponent(id)}`) {
|
|
442
528
|
await handleWrite(ctx, service, id);
|
|
443
529
|
return;
|
|
@@ -455,6 +541,18 @@ export async function handleWorkflowRoutes(ctx: WorkflowRouteContext): Promise<v
|
|
|
455
541
|
await handleToggle(ctx, service, id, false);
|
|
456
542
|
return;
|
|
457
543
|
}
|
|
544
|
+
if (id && method === 'POST' && path === `/workflows/${encodeURIComponent(id)}/run`) {
|
|
545
|
+
await handleRunWorkflow(ctx, service, id);
|
|
546
|
+
return;
|
|
547
|
+
}
|
|
548
|
+
if (
|
|
549
|
+
id &&
|
|
550
|
+
method === 'GET' &&
|
|
551
|
+
path === `/workflows/${encodeURIComponent(id)}/evaluation-samples`
|
|
552
|
+
) {
|
|
553
|
+
await handleEvaluationSamples(ctx, service, id);
|
|
554
|
+
return;
|
|
555
|
+
}
|
|
458
556
|
if (id && method === 'GET' && path === `/workflows/${encodeURIComponent(id)}/executions`) {
|
|
459
557
|
await handleListExecutions(ctx, service, id);
|
|
460
558
|
return;
|
package/src/routes/workflows.ts
CHANGED
|
@@ -44,8 +44,12 @@ async function listWorkflows(
|
|
|
44
44
|
): Promise<void> {
|
|
45
45
|
try {
|
|
46
46
|
const userId = req.query?.userId as string | undefined;
|
|
47
|
+
const q = req.query?.q as string | undefined;
|
|
47
48
|
const service = getService(runtime);
|
|
48
|
-
|
|
49
|
+
// `?q=` performs a ranked free-text search; otherwise list all (#8913).
|
|
50
|
+
const workflows = q?.trim()
|
|
51
|
+
? await service.searchWorkflows(q, userId)
|
|
52
|
+
: await service.listWorkflows(userId);
|
|
49
53
|
res.json({ success: true, data: workflows });
|
|
50
54
|
} catch (error) {
|
|
51
55
|
res.status(500).json({
|