@elizaos/plugin-workflow 2.0.0-beta.1
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/README.md +71 -0
- package/auto-enable.ts +18 -0
- package/dist/actions/index.d.ts +2 -0
- package/dist/actions/index.d.ts.map +1 -0
- package/dist/actions/index.js +2 -0
- package/dist/actions/index.js.map +1 -0
- package/dist/actions/workflow.d.ts +23 -0
- package/dist/actions/workflow.d.ts.map +1 -0
- package/dist/actions/workflow.js +425 -0
- package/dist/actions/workflow.js.map +1 -0
- package/dist/data/defaultNodes.json +9887 -0
- package/dist/data/schemaIndex.json +1 -0
- package/dist/data/triggerSchemaIndex.json +1 -0
- package/dist/db/index.d.ts +2 -0
- package/dist/db/index.d.ts.map +1 -0
- package/dist/db/index.js +2 -0
- package/dist/db/index.js.map +1 -0
- package/dist/db/schema.d.ts +588 -0
- package/dist/db/schema.d.ts.map +1 -0
- package/dist/db/schema.js +59 -0
- package/dist/db/schema.js.map +1 -0
- package/dist/index.d.ts +34 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +126 -0
- package/dist/index.js.map +1 -0
- package/dist/lib/automations-builder.d.ts +21 -0
- package/dist/lib/automations-builder.d.ts.map +1 -0
- package/dist/lib/automations-builder.js +557 -0
- package/dist/lib/automations-builder.js.map +1 -0
- package/dist/lib/automations-types.d.ts +153 -0
- package/dist/lib/automations-types.d.ts.map +1 -0
- package/dist/lib/automations-types.js +191 -0
- package/dist/lib/automations-types.js.map +1 -0
- package/dist/lib/index.d.ts +3 -0
- package/dist/lib/index.d.ts.map +1 -0
- package/dist/lib/index.js +3 -0
- package/dist/lib/index.js.map +1 -0
- package/dist/lib/legacy-task-migration.d.ts +20 -0
- package/dist/lib/legacy-task-migration.d.ts.map +1 -0
- package/dist/lib/legacy-task-migration.js +110 -0
- package/dist/lib/legacy-task-migration.js.map +1 -0
- package/dist/lib/legacy-text-trigger-migration.d.ts +18 -0
- package/dist/lib/legacy-text-trigger-migration.d.ts.map +1 -0
- package/dist/lib/legacy-text-trigger-migration.js +131 -0
- package/dist/lib/legacy-text-trigger-migration.js.map +1 -0
- package/dist/lib/workflow-clarification.d.ts +113 -0
- package/dist/lib/workflow-clarification.d.ts.map +1 -0
- package/dist/lib/workflow-clarification.js +425 -0
- package/dist/lib/workflow-clarification.js.map +1 -0
- package/dist/plugin-routes.d.ts +9 -0
- package/dist/plugin-routes.d.ts.map +1 -0
- package/dist/plugin-routes.js +147 -0
- package/dist/plugin-routes.js.map +1 -0
- package/dist/providers/activeWorkflows.d.ts +11 -0
- package/dist/providers/activeWorkflows.d.ts.map +1 -0
- package/dist/providers/activeWorkflows.js +72 -0
- package/dist/providers/activeWorkflows.js.map +1 -0
- package/dist/providers/index.d.ts +4 -0
- package/dist/providers/index.d.ts.map +1 -0
- package/dist/providers/index.js +4 -0
- package/dist/providers/index.js.map +1 -0
- package/dist/providers/pendingDraft.d.ts +9 -0
- package/dist/providers/pendingDraft.d.ts.map +1 -0
- package/dist/providers/pendingDraft.js +48 -0
- package/dist/providers/pendingDraft.js.map +1 -0
- package/dist/providers/workflowStatus.d.ts +3 -0
- package/dist/providers/workflowStatus.d.ts.map +1 -0
- package/dist/providers/workflowStatus.js +69 -0
- package/dist/providers/workflowStatus.js.map +1 -0
- package/dist/register-routes.d.ts +2 -0
- package/dist/register-routes.d.ts.map +1 -0
- package/dist/register-routes.js +6 -0
- package/dist/register-routes.js.map +1 -0
- package/dist/routes/_helpers.d.ts +11 -0
- package/dist/routes/_helpers.d.ts.map +1 -0
- package/dist/routes/_helpers.js +22 -0
- package/dist/routes/_helpers.js.map +1 -0
- package/dist/routes/automations.d.ts +19 -0
- package/dist/routes/automations.d.ts.map +1 -0
- package/dist/routes/automations.js +32 -0
- package/dist/routes/automations.js.map +1 -0
- package/dist/routes/embedded-webhooks.d.ts +3 -0
- package/dist/routes/embedded-webhooks.d.ts.map +1 -0
- package/dist/routes/embedded-webhooks.js +47 -0
- package/dist/routes/embedded-webhooks.js.map +1 -0
- package/dist/routes/executions.d.ts +3 -0
- package/dist/routes/executions.d.ts.map +1 -0
- package/dist/routes/executions.js +58 -0
- package/dist/routes/executions.js.map +1 -0
- package/dist/routes/index.d.ts +4 -0
- package/dist/routes/index.d.ts.map +1 -0
- package/dist/routes/index.js +14 -0
- package/dist/routes/index.js.map +1 -0
- package/dist/routes/nodes.d.ts +3 -0
- package/dist/routes/nodes.d.ts.map +1 -0
- package/dist/routes/nodes.js +168 -0
- package/dist/routes/nodes.js.map +1 -0
- package/dist/routes/validation.d.ts +3 -0
- package/dist/routes/validation.d.ts.map +1 -0
- package/dist/routes/validation.js +41 -0
- package/dist/routes/validation.js.map +1 -0
- package/dist/routes/workflow-routes.d.ts +27 -0
- package/dist/routes/workflow-routes.d.ts.map +1 -0
- package/dist/routes/workflow-routes.js +326 -0
- package/dist/routes/workflow-routes.js.map +1 -0
- package/dist/routes/workflows.d.ts +3 -0
- package/dist/routes/workflows.d.ts.map +1 -0
- package/dist/routes/workflows.js +252 -0
- package/dist/routes/workflows.js.map +1 -0
- package/dist/schemas/draftIntent.d.ts +22 -0
- package/dist/schemas/draftIntent.d.ts.map +1 -0
- package/dist/schemas/draftIntent.js +22 -0
- package/dist/schemas/draftIntent.js.map +1 -0
- package/dist/schemas/feasibility.d.ts +13 -0
- package/dist/schemas/feasibility.d.ts.map +1 -0
- package/dist/schemas/feasibility.js +9 -0
- package/dist/schemas/feasibility.js.map +1 -0
- package/dist/schemas/index.d.ts +5 -0
- package/dist/schemas/index.d.ts.map +1 -0
- package/dist/schemas/index.js +5 -0
- package/dist/schemas/index.js.map +1 -0
- package/dist/schemas/keywordExtraction.d.ts +14 -0
- package/dist/schemas/keywordExtraction.d.ts.map +1 -0
- package/dist/schemas/keywordExtraction.js +12 -0
- package/dist/schemas/keywordExtraction.js.map +1 -0
- package/dist/schemas/workflowMatching.d.ts +36 -0
- package/dist/schemas/workflowMatching.d.ts.map +1 -0
- package/dist/schemas/workflowMatching.js +30 -0
- package/dist/schemas/workflowMatching.js.map +1 -0
- package/dist/services/embedded-workflow-service.d.ts +106 -0
- package/dist/services/embedded-workflow-service.d.ts.map +1 -0
- package/dist/services/embedded-workflow-service.js +1900 -0
- package/dist/services/embedded-workflow-service.js.map +1 -0
- package/dist/services/index.d.ts +5 -0
- package/dist/services/index.d.ts.map +1 -0
- package/dist/services/index.js +5 -0
- package/dist/services/index.js.map +1 -0
- package/dist/services/workflow-credential-store.d.ts +27 -0
- package/dist/services/workflow-credential-store.d.ts.map +1 -0
- package/dist/services/workflow-credential-store.js +92 -0
- package/dist/services/workflow-credential-store.js.map +1 -0
- package/dist/services/workflow-dispatch.d.ts +41 -0
- package/dist/services/workflow-dispatch.d.ts.map +1 -0
- package/dist/services/workflow-dispatch.js +86 -0
- package/dist/services/workflow-dispatch.js.map +1 -0
- package/dist/services/workflow-service.d.ts +63 -0
- package/dist/services/workflow-service.d.ts.map +1 -0
- package/dist/services/workflow-service.js +492 -0
- package/dist/services/workflow-service.js.map +1 -0
- package/dist/trigger-routes.d.ts +153 -0
- package/dist/trigger-routes.d.ts.map +1 -0
- package/dist/trigger-routes.js +424 -0
- package/dist/trigger-routes.js.map +1 -0
- package/dist/types/index.d.ts +457 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/index.js +59 -0
- package/dist/types/index.js.map +1 -0
- package/dist/utils/catalog.d.ts +16 -0
- package/dist/utils/catalog.d.ts.map +1 -0
- package/dist/utils/catalog.js +211 -0
- package/dist/utils/catalog.js.map +1 -0
- package/dist/utils/clarification.d.ts +17 -0
- package/dist/utils/clarification.d.ts.map +1 -0
- package/dist/utils/clarification.js +46 -0
- package/dist/utils/clarification.js.map +1 -0
- package/dist/utils/context.d.ts +4 -0
- package/dist/utils/context.d.ts.map +1 -0
- package/dist/utils/context.js +18 -0
- package/dist/utils/context.js.map +1 -0
- package/dist/utils/credentialResolver.d.ts +22 -0
- package/dist/utils/credentialResolver.d.ts.map +1 -0
- package/dist/utils/credentialResolver.js +146 -0
- package/dist/utils/credentialResolver.js.map +1 -0
- package/dist/utils/generation.d.ts +36 -0
- package/dist/utils/generation.d.ts.map +1 -0
- package/dist/utils/generation.js +701 -0
- package/dist/utils/generation.js.map +1 -0
- package/dist/utils/host-capabilities.d.ts +27 -0
- package/dist/utils/host-capabilities.d.ts.map +1 -0
- package/dist/utils/host-capabilities.js +59 -0
- package/dist/utils/host-capabilities.js.map +1 -0
- package/dist/utils/inferSyntheticOutputSchema.d.ts +20 -0
- package/dist/utils/inferSyntheticOutputSchema.d.ts.map +1 -0
- package/dist/utils/inferSyntheticOutputSchema.js +151 -0
- package/dist/utils/inferSyntheticOutputSchema.js.map +1 -0
- package/dist/utils/outputSchema.d.ts +26 -0
- package/dist/utils/outputSchema.d.ts.map +1 -0
- package/dist/utils/outputSchema.js +297 -0
- package/dist/utils/outputSchema.js.map +1 -0
- package/dist/utils/validateAndRepair.d.ts +41 -0
- package/dist/utils/validateAndRepair.d.ts.map +1 -0
- package/dist/utils/validateAndRepair.js +483 -0
- package/dist/utils/validateAndRepair.js.map +1 -0
- package/dist/utils/workflow-prompts/actionResponse.d.ts +2 -0
- package/dist/utils/workflow-prompts/actionResponse.d.ts.map +1 -0
- package/dist/utils/workflow-prompts/actionResponse.js +17 -0
- package/dist/utils/workflow-prompts/actionResponse.js.map +1 -0
- package/dist/utils/workflow-prompts/draftIntent.d.ts +2 -0
- package/dist/utils/workflow-prompts/draftIntent.d.ts.map +1 -0
- package/dist/utils/workflow-prompts/draftIntent.js +23 -0
- package/dist/utils/workflow-prompts/draftIntent.js.map +1 -0
- package/dist/utils/workflow-prompts/feasibilityCheck.d.ts +2 -0
- package/dist/utils/workflow-prompts/feasibilityCheck.d.ts.map +1 -0
- package/dist/utils/workflow-prompts/feasibilityCheck.js +21 -0
- package/dist/utils/workflow-prompts/feasibilityCheck.js.map +1 -0
- package/dist/utils/workflow-prompts/fieldCorrection.d.ts +3 -0
- package/dist/utils/workflow-prompts/fieldCorrection.d.ts.map +1 -0
- package/dist/utils/workflow-prompts/fieldCorrection.js +20 -0
- package/dist/utils/workflow-prompts/fieldCorrection.js.map +1 -0
- package/dist/utils/workflow-prompts/index.d.ts +8 -0
- package/dist/utils/workflow-prompts/index.d.ts.map +1 -0
- package/dist/utils/workflow-prompts/index.js +8 -0
- package/dist/utils/workflow-prompts/index.js.map +1 -0
- package/dist/utils/workflow-prompts/keywordExtraction.d.ts +2 -0
- package/dist/utils/workflow-prompts/keywordExtraction.d.ts.map +1 -0
- package/dist/utils/workflow-prompts/keywordExtraction.js +21 -0
- package/dist/utils/workflow-prompts/keywordExtraction.js.map +1 -0
- package/dist/utils/workflow-prompts/parameterCorrection.d.ts +3 -0
- package/dist/utils/workflow-prompts/parameterCorrection.d.ts.map +1 -0
- package/dist/utils/workflow-prompts/parameterCorrection.js +29 -0
- package/dist/utils/workflow-prompts/parameterCorrection.js.map +1 -0
- package/dist/utils/workflow-prompts/workflowGeneration.d.ts +2 -0
- package/dist/utils/workflow-prompts/workflowGeneration.d.ts.map +1 -0
- package/dist/utils/workflow-prompts/workflowGeneration.js +529 -0
- package/dist/utils/workflow-prompts/workflowGeneration.js.map +1 -0
- package/dist/utils/workflow-prompts/workflowMatching.d.ts +2 -0
- package/dist/utils/workflow-prompts/workflowMatching.d.ts.map +1 -0
- package/dist/utils/workflow-prompts/workflowMatching.js +23 -0
- package/dist/utils/workflow-prompts/workflowMatching.js.map +1 -0
- package/dist/utils/workflow.d.ts +62 -0
- package/dist/utils/workflow.d.ts.map +1 -0
- package/dist/utils/workflow.js +712 -0
- package/dist/utils/workflow.js.map +1 -0
- package/package.json +87 -0
- package/src/actions/index.ts +1 -0
- package/src/actions/workflow.ts +494 -0
- package/src/data/defaultNodes.json +9887 -0
- package/src/data/schemaIndex.json +1 -0
- package/src/data/triggerSchemaIndex.json +1 -0
- package/src/db/index.ts +8 -0
- package/src/db/schema.ts +94 -0
- package/src/index.ts +179 -0
- package/src/lib/automations-builder.ts +679 -0
- package/src/lib/automations-types.ts +391 -0
- package/src/lib/index.ts +8 -0
- package/src/lib/legacy-task-migration.ts +143 -0
- package/src/lib/legacy-text-trigger-migration.ts +178 -0
- package/src/lib/workflow-clarification.ts +497 -0
- package/src/plugin-routes.ts +164 -0
- package/src/providers/activeWorkflows.ts +81 -0
- package/src/providers/index.ts +3 -0
- package/src/providers/pendingDraft.ts +55 -0
- package/src/providers/workflowStatus.ts +88 -0
- package/src/register-routes.ts +6 -0
- package/src/routes/_helpers.ts +27 -0
- package/src/routes/automations.ts +46 -0
- package/src/routes/embedded-webhooks.ts +64 -0
- package/src/routes/executions.ts +75 -0
- package/src/routes/index.ts +16 -0
- package/src/routes/nodes.ts +211 -0
- package/src/routes/validation.ts +51 -0
- package/src/routes/workflow-routes.ts +469 -0
- package/src/routes/workflows.ts +310 -0
- package/src/schemas/draftIntent.ts +21 -0
- package/src/schemas/feasibility.ts +8 -0
- package/src/schemas/index.ts +4 -0
- package/src/schemas/keywordExtraction.ts +11 -0
- package/src/schemas/workflowMatching.ts +29 -0
- package/src/services/embedded-workflow-service.ts +2224 -0
- package/src/services/index.ts +17 -0
- package/src/services/workflow-credential-store.ts +132 -0
- package/src/services/workflow-dispatch.ts +121 -0
- package/src/services/workflow-service.ts +839 -0
- package/src/trigger-routes.ts +714 -0
- package/src/types/index.ts +562 -0
- package/src/utils/catalog.ts +260 -0
- package/src/utils/clarification.ts +52 -0
- package/src/utils/context.ts +22 -0
- package/src/utils/credentialResolver.ts +234 -0
- package/src/utils/generation.ts +987 -0
- package/src/utils/host-capabilities.ts +81 -0
- package/src/utils/inferSyntheticOutputSchema.ts +163 -0
- package/src/utils/outputSchema.ts +372 -0
- package/src/utils/validateAndRepair.ts +610 -0
- package/src/utils/workflow-prompts/actionResponse.ts +16 -0
- package/src/utils/workflow-prompts/draftIntent.ts +22 -0
- package/src/utils/workflow-prompts/feasibilityCheck.ts +20 -0
- package/src/utils/workflow-prompts/fieldCorrection.ts +20 -0
- package/src/utils/workflow-prompts/index.ts +10 -0
- package/src/utils/workflow-prompts/keywordExtraction.ts +20 -0
- package/src/utils/workflow-prompts/parameterCorrection.ts +29 -0
- package/src/utils/workflow-prompts/workflowGeneration.ts +528 -0
- package/src/utils/workflow-prompts/workflowMatching.ts +22 -0
- package/src/utils/workflow.ts +895 -0
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Idempotent migration: legacy `kind: "text"` trigger Tasks → workflow triggers
|
|
3
|
+
*
|
|
4
|
+
* Walks every TRIGGER_DISPATCH task for the running agent. For each task whose
|
|
5
|
+
* stored `metadata.trigger.kind` is `"text"` (or omitted, which is the legacy
|
|
6
|
+
* default), deploy a one-node workflow built around
|
|
7
|
+
* `workflows-nodes-base.respondToEvent` and rewrite the trigger metadata so
|
|
8
|
+
* `kind = "workflow"` plus a pointer to the new workflow. A `migratedFromText`
|
|
9
|
+
* marker prevents double-conversion on subsequent boots.
|
|
10
|
+
*/
|
|
11
|
+
import { logger, } from '@elizaos/core';
|
|
12
|
+
import { WORKFLOW_SERVICE_TYPE } from '../services/workflow-service';
|
|
13
|
+
const TRIGGER_TASK_NAME = 'TRIGGER_DISPATCH';
|
|
14
|
+
const RESPOND_TO_EVENT_NODE_TYPE = 'workflows-nodes-base.respondToEvent';
|
|
15
|
+
function readWorkflowService(runtime) {
|
|
16
|
+
const svc = runtime.getService(WORKFLOW_SERVICE_TYPE);
|
|
17
|
+
return svc ?? null;
|
|
18
|
+
}
|
|
19
|
+
function readTriggerFromMetadata(task) {
|
|
20
|
+
const trigger = task.metadata?.trigger;
|
|
21
|
+
if (!trigger || typeof trigger !== 'object' || Array.isArray(trigger))
|
|
22
|
+
return null;
|
|
23
|
+
if (typeof trigger.triggerId !== 'string')
|
|
24
|
+
return null;
|
|
25
|
+
return trigger;
|
|
26
|
+
}
|
|
27
|
+
function buildRespondToEventWorkflow(trigger, fallbackName) {
|
|
28
|
+
const displayName = typeof trigger.displayName === 'string' && trigger.displayName.trim().length > 0
|
|
29
|
+
? trigger.displayName
|
|
30
|
+
: fallbackName;
|
|
31
|
+
const instructions = typeof trigger.instructions === 'string' && trigger.instructions.trim().length > 0
|
|
32
|
+
? trigger.instructions
|
|
33
|
+
: displayName;
|
|
34
|
+
return {
|
|
35
|
+
name: displayName,
|
|
36
|
+
nodes: [
|
|
37
|
+
{
|
|
38
|
+
id: 'respond-to-event',
|
|
39
|
+
name: 'Respond To Event',
|
|
40
|
+
type: RESPOND_TO_EVENT_NODE_TYPE,
|
|
41
|
+
typeVersion: 1,
|
|
42
|
+
position: [0, 0],
|
|
43
|
+
parameters: {
|
|
44
|
+
instructions,
|
|
45
|
+
displayName,
|
|
46
|
+
wakeMode: 'inject_now',
|
|
47
|
+
},
|
|
48
|
+
},
|
|
49
|
+
],
|
|
50
|
+
connections: {},
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
export async function migrateLegacyTextTriggers(runtime) {
|
|
54
|
+
const summary = { migrated: 0, skipped: 0, failed: 0 };
|
|
55
|
+
const service = readWorkflowService(runtime);
|
|
56
|
+
if (!service) {
|
|
57
|
+
logger.debug({ src: 'plugin:workflow:migration:text-trigger' }, 'WorkflowService not registered; skipping text-trigger migration');
|
|
58
|
+
return summary;
|
|
59
|
+
}
|
|
60
|
+
let tasks;
|
|
61
|
+
try {
|
|
62
|
+
tasks = await runtime.getTasksByName(TRIGGER_TASK_NAME);
|
|
63
|
+
}
|
|
64
|
+
catch (err) {
|
|
65
|
+
logger.warn({
|
|
66
|
+
src: 'plugin:workflow:migration:text-trigger',
|
|
67
|
+
err: err instanceof Error ? err.message : String(err),
|
|
68
|
+
}, 'Failed to list trigger dispatch tasks; aborting migration');
|
|
69
|
+
return summary;
|
|
70
|
+
}
|
|
71
|
+
for (const task of tasks) {
|
|
72
|
+
if (!task.id || task.agentId !== runtime.agentId) {
|
|
73
|
+
summary.skipped += 1;
|
|
74
|
+
continue;
|
|
75
|
+
}
|
|
76
|
+
const metadata = task.metadata ?? {};
|
|
77
|
+
if (metadata.migratedFromText === true) {
|
|
78
|
+
summary.skipped += 1;
|
|
79
|
+
continue;
|
|
80
|
+
}
|
|
81
|
+
const trigger = readTriggerFromMetadata(task);
|
|
82
|
+
if (!trigger) {
|
|
83
|
+
summary.skipped += 1;
|
|
84
|
+
continue;
|
|
85
|
+
}
|
|
86
|
+
// `kind` is optional in the legacy schema; absence implies "text".
|
|
87
|
+
const isTextKind = trigger.kind === 'text' || trigger.kind === undefined;
|
|
88
|
+
if (!isTextKind) {
|
|
89
|
+
summary.skipped += 1;
|
|
90
|
+
continue;
|
|
91
|
+
}
|
|
92
|
+
try {
|
|
93
|
+
const draft = buildRespondToEventWorkflow(trigger, task.name ?? 'Trigger');
|
|
94
|
+
const deployed = await service.deployWorkflow(draft, runtime.agentId);
|
|
95
|
+
if (!deployed.id) {
|
|
96
|
+
summary.failed += 1;
|
|
97
|
+
logger.warn({
|
|
98
|
+
src: 'plugin:workflow:migration:text-trigger',
|
|
99
|
+
taskId: task.id,
|
|
100
|
+
triggerId: trigger.triggerId,
|
|
101
|
+
}, 'deployWorkflow returned no id; will retry on next boot');
|
|
102
|
+
continue;
|
|
103
|
+
}
|
|
104
|
+
const updatedTrigger = {
|
|
105
|
+
...trigger,
|
|
106
|
+
kind: 'workflow',
|
|
107
|
+
workflowId: deployed.id,
|
|
108
|
+
workflowName: deployed.name,
|
|
109
|
+
};
|
|
110
|
+
const nextMetadata = {
|
|
111
|
+
...metadata,
|
|
112
|
+
trigger: updatedTrigger,
|
|
113
|
+
migratedFromText: true,
|
|
114
|
+
migratedAt: Date.now(),
|
|
115
|
+
};
|
|
116
|
+
await runtime.updateTask(task.id, { metadata: nextMetadata });
|
|
117
|
+
summary.migrated += 1;
|
|
118
|
+
}
|
|
119
|
+
catch (err) {
|
|
120
|
+
summary.failed += 1;
|
|
121
|
+
logger.warn({
|
|
122
|
+
src: 'plugin:workflow:migration:text-trigger',
|
|
123
|
+
taskId: task.id,
|
|
124
|
+
triggerId: trigger.triggerId,
|
|
125
|
+
err: err instanceof Error ? err.message : String(err),
|
|
126
|
+
}, 'Failed to migrate text trigger to workflow trigger');
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
return summary;
|
|
130
|
+
}
|
|
131
|
+
//# sourceMappingURL=legacy-text-trigger-migration.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"legacy-text-trigger-migration.js","sourceRoot":"","sources":["../../src/lib/legacy-text-trigger-migration.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Clarification helpers for workflow generation routes.
|
|
3
|
+
*
|
|
4
|
+
* - `coerceClarifications`: normalizes the plugin's mixed-shape
|
|
5
|
+
* `_meta.requiresClarification` (legacy strings + structured objects)
|
|
6
|
+
* into typed `WorkflowClarificationRequest[]`.
|
|
7
|
+
* - `setByDotPath`: applies `{paramPath, value}` resolutions to a draft
|
|
8
|
+
* workflow JSON in place. Supports dot segments and bracketed-string
|
|
9
|
+
* segments (`nodes["Discord Send"].parameters.channelId`).
|
|
10
|
+
*
|
|
11
|
+
* Kept out of `workflows-routes.ts` so the handlers stay focused on transport.
|
|
12
|
+
*/
|
|
13
|
+
export interface WorkflowClarificationRequest {
|
|
14
|
+
kind: 'target_channel' | 'target_server' | 'recipient' | 'value' | 'free_text';
|
|
15
|
+
platform?: string;
|
|
16
|
+
scope?: {
|
|
17
|
+
guildId?: string;
|
|
18
|
+
};
|
|
19
|
+
question: string;
|
|
20
|
+
paramPath: string;
|
|
21
|
+
}
|
|
22
|
+
export interface WorkflowClarificationResolution {
|
|
23
|
+
paramPath: string;
|
|
24
|
+
value: string;
|
|
25
|
+
}
|
|
26
|
+
export interface WorkflowClarificationTargetGroup {
|
|
27
|
+
platform: string;
|
|
28
|
+
groupId: string;
|
|
29
|
+
groupName: string;
|
|
30
|
+
targets: Array<{
|
|
31
|
+
id: string;
|
|
32
|
+
name: string;
|
|
33
|
+
kind: 'channel' | 'recipient' | 'chat';
|
|
34
|
+
}>;
|
|
35
|
+
}
|
|
36
|
+
export declare function coerceClarifications(raw: unknown): WorkflowClarificationRequest[];
|
|
37
|
+
/**
|
|
38
|
+
* Tokenizer for paramPath. Handles three segment forms:
|
|
39
|
+
* - dot identifier: `parameters`
|
|
40
|
+
* - bracketed quoted key: `["Discord Send"]` or `['k']`
|
|
41
|
+
* - bracketed numeric: `[0]`
|
|
42
|
+
*/
|
|
43
|
+
export declare function parseParamPath(path: string): string[];
|
|
44
|
+
/**
|
|
45
|
+
* Mutate `obj` so that its value at `paramPath` becomes `value`. Creates
|
|
46
|
+
* intermediate plain objects as needed; never replaces an existing
|
|
47
|
+
* non-object intermediate (those throw, since the path is invalid).
|
|
48
|
+
*
|
|
49
|
+
* Segments that hit an array can be:
|
|
50
|
+
* - numeric → direct index
|
|
51
|
+
* - non-numeric (string) → looked up against the array's `.name` or `.id`
|
|
52
|
+
* field. Common in LLM output: the model writes `nodes["Post to Slack"]`
|
|
53
|
+
* rather than `nodes[2]`. Treating that as a hard failure forced every
|
|
54
|
+
* clarification resolution through a 400.
|
|
55
|
+
*
|
|
56
|
+
* If the segment expects an array but the existing intermediate is a non-
|
|
57
|
+
* array object, we treat it as an object key (workflow shapes mix arrays and
|
|
58
|
+
* objects fairly freely; we err on the side of preserving structure).
|
|
59
|
+
*
|
|
60
|
+
* Terminal-segment guard: refuses to overwrite an existing object with a
|
|
61
|
+
* non-object value. The LLM sometimes emits a paramPath that points at a
|
|
62
|
+
* parent scope rather than a leaf (e.g.
|
|
63
|
+
* `nodes["Hourly Trigger"].parameters` for a question whose answer is a
|
|
64
|
+
* channel name); naively writing the string there replaces the entire
|
|
65
|
+
* `parameters` object and the workflow runner then rejects the workflow with
|
|
66
|
+
* `parameters must be object`. Throwing here gives `applyResolutions` a
|
|
67
|
+
* chance to fall back to the userNotes path.
|
|
68
|
+
*/
|
|
69
|
+
export declare function setByDotPath(obj: Record<string, unknown>, paramPath: string, value: unknown): void;
|
|
70
|
+
export declare function applyResolutions(draft: Record<string, unknown>, resolutions: ReadonlyArray<WorkflowClarificationResolution>): {
|
|
71
|
+
ok: true;
|
|
72
|
+
} | {
|
|
73
|
+
ok: false;
|
|
74
|
+
error: string;
|
|
75
|
+
paramPath?: string;
|
|
76
|
+
};
|
|
77
|
+
/**
|
|
78
|
+
* Drop the resolved clarifications from the draft's `_meta` so the next
|
|
79
|
+
* read of the draft does not re-prompt the user for the same parameter.
|
|
80
|
+
*
|
|
81
|
+
* Two pruning paths:
|
|
82
|
+
* 1. Object-form clarifications with paramPath → prune by paramPath match.
|
|
83
|
+
* 2. String-form clarifications (LLM emits free-form questions without a
|
|
84
|
+
* paramPath) and object-form clarifications with empty paramPath →
|
|
85
|
+
* prune positionally by `freeFormCount` (UI presents them in order, so
|
|
86
|
+
* each free-form resolution consumes the next one).
|
|
87
|
+
*
|
|
88
|
+
* Positional-pruning contract: free-form items are dropped from the head of
|
|
89
|
+
* the stored list in order. The UI must therefore submit answers in the
|
|
90
|
+
* order they were presented, with no skipped or out-of-order items in a
|
|
91
|
+
* single batch — otherwise the wrong question gets pruned. If we ever need
|
|
92
|
+
* to support partial/interleaved submissions, switch the resolution payload
|
|
93
|
+
* to send the answered question text and match by value here instead.
|
|
94
|
+
*/
|
|
95
|
+
export declare function pruneResolvedClarifications(draft: Record<string, unknown>, resolved: ReadonlySet<string>, freeFormCount?: number): void;
|
|
96
|
+
/**
|
|
97
|
+
* Subset of `ElizaConnectorTargetCatalog` used by the route. Declared here
|
|
98
|
+
* (vs. imported from the service) so route tests can stub it without
|
|
99
|
+
* spinning up the full service.
|
|
100
|
+
*/
|
|
101
|
+
export interface CatalogLike {
|
|
102
|
+
listGroups(opts?: {
|
|
103
|
+
platform?: string;
|
|
104
|
+
groupId?: string;
|
|
105
|
+
}): Promise<WorkflowClarificationTargetGroup[]>;
|
|
106
|
+
}
|
|
107
|
+
/**
|
|
108
|
+
* Build a catalog snapshot for the platforms referenced by `clarifications`.
|
|
109
|
+
* If multiple clarifications reference the same platform, we union their
|
|
110
|
+
* groupId scopes — broader queries (no scope) always win.
|
|
111
|
+
*/
|
|
112
|
+
export declare function buildCatalogSnapshot(catalog: CatalogLike, clarifications: ReadonlyArray<WorkflowClarificationRequest>): Promise<WorkflowClarificationTargetGroup[]>;
|
|
113
|
+
//# sourceMappingURL=workflow-clarification.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"workflow-clarification.d.ts","sourceRoot":"","sources":["../../src/lib/workflow-clarification.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAIH,MAAM,WAAW,4BAA4B;IAC3C,IAAI,EAAE,gBAAgB,GAAG,eAAe,GAAG,WAAW,GAAG,OAAO,GAAG,WAAW,CAAC;IAC/E,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,KAAK,CAAC,EAAE;QAAE,OAAO,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IAC7B,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,+BAA+B;IAC9C,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,gCAAgC;IAC/C,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,KAAK,CAAC;QACb,EAAE,EAAE,MAAM,CAAC;QACX,IAAI,EAAE,MAAM,CAAC;QACb,IAAI,EAAE,SAAS,GAAG,WAAW,GAAG,MAAM,CAAC;KACxC,CAAC,CAAC;CACJ;AAkDD,wBAAgB,oBAAoB,CAAC,GAAG,EAAE,OAAO,GAAG,4BAA4B,EAAE,CAyCjF;AAED;;;;;GAKG;AACH,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,EAAE,CAiDrD;AAqBD;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,wBAAgB,YAAY,CAC1B,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC5B,SAAS,EAAE,MAAM,EACjB,KAAK,EAAE,OAAO,GACb,IAAI,CAoEN;AA2BD,wBAAgB,gBAAgB,CAC9B,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC9B,WAAW,EAAE,aAAa,CAAC,+BAA+B,CAAC,GAC1D;IAAE,EAAE,EAAE,IAAI,CAAA;CAAE,GAAG;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,SAAS,CAAC,EAAE,MAAM,CAAA;CAAE,CAyDjE;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,2BAA2B,CACzC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC9B,QAAQ,EAAE,WAAW,CAAC,MAAM,CAAC,EAC7B,aAAa,SAAI,GAChB,IAAI,CAoCN;AAED;;;;GAIG;AACH,MAAM,WAAW,WAAW;IAC1B,UAAU,CAAC,IAAI,CAAC,EAAE;QAChB,QAAQ,CAAC,EAAE,MAAM,CAAC;QAClB,OAAO,CAAC,EAAE,MAAM,CAAC;KAClB,GAAG,OAAO,CAAC,gCAAgC,EAAE,CAAC,CAAC;CACjD;AAED;;;;GAIG;AACH,wBAAsB,oBAAoB,CACxC,OAAO,EAAE,WAAW,EACpB,cAAc,EAAE,aAAa,CAAC,4BAA4B,CAAC,GAC1D,OAAO,CAAC,gCAAgC,EAAE,CAAC,CAwB7C"}
|
|
@@ -0,0 +1,425 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Clarification helpers for workflow generation routes.
|
|
3
|
+
*
|
|
4
|
+
* - `coerceClarifications`: normalizes the plugin's mixed-shape
|
|
5
|
+
* `_meta.requiresClarification` (legacy strings + structured objects)
|
|
6
|
+
* into typed `WorkflowClarificationRequest[]`.
|
|
7
|
+
* - `setByDotPath`: applies `{paramPath, value}` resolutions to a draft
|
|
8
|
+
* workflow JSON in place. Supports dot segments and bracketed-string
|
|
9
|
+
* segments (`nodes["Discord Send"].parameters.channelId`).
|
|
10
|
+
*
|
|
11
|
+
* Kept out of `workflows-routes.ts` so the handlers stay focused on transport.
|
|
12
|
+
*/
|
|
13
|
+
import { logger } from '@elizaos/core';
|
|
14
|
+
const VALID_KINDS = new Set([
|
|
15
|
+
'target_channel',
|
|
16
|
+
'target_server',
|
|
17
|
+
'recipient',
|
|
18
|
+
'value',
|
|
19
|
+
'free_text',
|
|
20
|
+
]);
|
|
21
|
+
/**
|
|
22
|
+
* Stable sort priority for clarification kinds. Lower number = asked first.
|
|
23
|
+
*
|
|
24
|
+
* `target_server` MUST come before `target_channel` because the channel
|
|
25
|
+
* picker reads `scope.guildId` from the server pick to narrow its options.
|
|
26
|
+
* If the LLM emits them in reverse order (which it sometimes does), the
|
|
27
|
+
* user picks a channel first against an unscoped catalog, which is bad UX
|
|
28
|
+
* (every channel from every guild they belong to) and can land the wrong
|
|
29
|
+
* id when channel names collide across guilds.
|
|
30
|
+
*
|
|
31
|
+
* `recipient` shares the server-scoped concern — DMs/contacts belong to a
|
|
32
|
+
* platform context — so it sorts after `target_server` too. `value` and
|
|
33
|
+
* `free_text` don't depend on prior picks; relative order is preserved
|
|
34
|
+
* because Array.prototype.sort is stable as of ES2019.
|
|
35
|
+
*/
|
|
36
|
+
const KIND_SORT_PRIORITY = {
|
|
37
|
+
target_server: 0,
|
|
38
|
+
target_channel: 1,
|
|
39
|
+
recipient: 1,
|
|
40
|
+
value: 2,
|
|
41
|
+
free_text: 3,
|
|
42
|
+
};
|
|
43
|
+
function isStructuredClarification(v) {
|
|
44
|
+
if (!v || typeof v !== 'object') {
|
|
45
|
+
return false;
|
|
46
|
+
}
|
|
47
|
+
const o = v;
|
|
48
|
+
if (typeof o.question !== 'string' || o.question.trim().length === 0) {
|
|
49
|
+
return false;
|
|
50
|
+
}
|
|
51
|
+
// `kind` and `paramPath` may be missing on partial / older payloads — we
|
|
52
|
+
// default them here rather than reject the item outright.
|
|
53
|
+
return true;
|
|
54
|
+
}
|
|
55
|
+
export function coerceClarifications(raw) {
|
|
56
|
+
if (!Array.isArray(raw) || raw.length === 0) {
|
|
57
|
+
return [];
|
|
58
|
+
}
|
|
59
|
+
const out = [];
|
|
60
|
+
for (const item of raw) {
|
|
61
|
+
if (typeof item === 'string') {
|
|
62
|
+
const trimmed = item.trim();
|
|
63
|
+
if (trimmed.length === 0) {
|
|
64
|
+
continue;
|
|
65
|
+
}
|
|
66
|
+
out.push({ kind: 'free_text', question: trimmed, paramPath: '' });
|
|
67
|
+
continue;
|
|
68
|
+
}
|
|
69
|
+
if (!isStructuredClarification(item)) {
|
|
70
|
+
continue;
|
|
71
|
+
}
|
|
72
|
+
const kindRaw = typeof item.kind === 'string' ? item.kind : 'free_text';
|
|
73
|
+
const kind = (VALID_KINDS.has(kindRaw) ? kindRaw : 'free_text');
|
|
74
|
+
const platform = typeof item.platform === 'string' ? item.platform : undefined;
|
|
75
|
+
let scope;
|
|
76
|
+
if (item.scope && typeof item.scope === 'object' && typeof item.scope.guildId === 'string') {
|
|
77
|
+
scope = {
|
|
78
|
+
guildId: item.scope.guildId,
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
const paramPath = typeof item.paramPath === 'string' ? item.paramPath : '';
|
|
82
|
+
out.push({
|
|
83
|
+
kind,
|
|
84
|
+
platform,
|
|
85
|
+
scope,
|
|
86
|
+
question: item.question.trim(),
|
|
87
|
+
paramPath,
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
// Stable-sort so dependency-bearing kinds come first. Within the same
|
|
91
|
+
// priority bucket the LLM's emission order is preserved.
|
|
92
|
+
out.sort((a, b) => KIND_SORT_PRIORITY[a.kind] - KIND_SORT_PRIORITY[b.kind]);
|
|
93
|
+
return out;
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* Tokenizer for paramPath. Handles three segment forms:
|
|
97
|
+
* - dot identifier: `parameters`
|
|
98
|
+
* - bracketed quoted key: `["Discord Send"]` or `['k']`
|
|
99
|
+
* - bracketed numeric: `[0]`
|
|
100
|
+
*/
|
|
101
|
+
export function parseParamPath(path) {
|
|
102
|
+
const segments = [];
|
|
103
|
+
let i = 0;
|
|
104
|
+
const n = path.length;
|
|
105
|
+
while (i < n) {
|
|
106
|
+
const ch = path[i];
|
|
107
|
+
if (ch === '.') {
|
|
108
|
+
i += 1;
|
|
109
|
+
continue;
|
|
110
|
+
}
|
|
111
|
+
if (ch === '[') {
|
|
112
|
+
const close = path.indexOf(']', i);
|
|
113
|
+
if (close < 0) {
|
|
114
|
+
throw new Error(`unterminated bracket at index ${i}`);
|
|
115
|
+
}
|
|
116
|
+
const inner = path.slice(i + 1, close).trim();
|
|
117
|
+
if (inner.length === 0) {
|
|
118
|
+
throw new Error(`empty bracket at index ${i}`);
|
|
119
|
+
}
|
|
120
|
+
const first = inner[0];
|
|
121
|
+
const last = inner[inner.length - 1];
|
|
122
|
+
if ((first === '"' && last === '"') || (first === "'" && last === "'")) {
|
|
123
|
+
segments.push(inner.slice(1, -1));
|
|
124
|
+
}
|
|
125
|
+
else if (/^[0-9]+$/.test(inner)) {
|
|
126
|
+
segments.push(inner);
|
|
127
|
+
}
|
|
128
|
+
else {
|
|
129
|
+
// Unquoted bare identifier inside brackets — accept to be lenient
|
|
130
|
+
// with LLM output (e.g. `[channelId]`).
|
|
131
|
+
segments.push(inner);
|
|
132
|
+
}
|
|
133
|
+
i = close + 1;
|
|
134
|
+
continue;
|
|
135
|
+
}
|
|
136
|
+
// Identifier run: read until next `.` or `[`.
|
|
137
|
+
let j = i;
|
|
138
|
+
while (j < n && path[j] !== '.' && path[j] !== '[') {
|
|
139
|
+
j += 1;
|
|
140
|
+
}
|
|
141
|
+
const ident = path.slice(i, j).trim();
|
|
142
|
+
if (ident.length === 0) {
|
|
143
|
+
throw new Error(`empty identifier at index ${i}`);
|
|
144
|
+
}
|
|
145
|
+
segments.push(ident);
|
|
146
|
+
i = j;
|
|
147
|
+
}
|
|
148
|
+
if (segments.length === 0) {
|
|
149
|
+
throw new Error('paramPath has no segments');
|
|
150
|
+
}
|
|
151
|
+
return segments;
|
|
152
|
+
}
|
|
153
|
+
/**
|
|
154
|
+
* Find the index of a named entry in an array of objects, matching against
|
|
155
|
+
* `.name` first then `.id`. Returns -1 if no match. Used by `setByDotPath`
|
|
156
|
+
* to resolve `nodes["My Node"]`-style segments — the LLM consistently
|
|
157
|
+
* addresses workflow nodes by their human name even though `workflow.nodes`
|
|
158
|
+
* is an array, so we map name → index here rather than rejecting the path.
|
|
159
|
+
*/
|
|
160
|
+
function findArrayIndexByNameOrId(arr, key) {
|
|
161
|
+
for (let i = 0; i < arr.length; i += 1) {
|
|
162
|
+
const entry = arr[i];
|
|
163
|
+
if (entry === null || typeof entry !== 'object')
|
|
164
|
+
continue;
|
|
165
|
+
const obj = entry;
|
|
166
|
+
if (obj.name === key || obj.id === key) {
|
|
167
|
+
return i;
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
return -1;
|
|
171
|
+
}
|
|
172
|
+
/**
|
|
173
|
+
* Mutate `obj` so that its value at `paramPath` becomes `value`. Creates
|
|
174
|
+
* intermediate plain objects as needed; never replaces an existing
|
|
175
|
+
* non-object intermediate (those throw, since the path is invalid).
|
|
176
|
+
*
|
|
177
|
+
* Segments that hit an array can be:
|
|
178
|
+
* - numeric → direct index
|
|
179
|
+
* - non-numeric (string) → looked up against the array's `.name` or `.id`
|
|
180
|
+
* field. Common in LLM output: the model writes `nodes["Post to Slack"]`
|
|
181
|
+
* rather than `nodes[2]`. Treating that as a hard failure forced every
|
|
182
|
+
* clarification resolution through a 400.
|
|
183
|
+
*
|
|
184
|
+
* If the segment expects an array but the existing intermediate is a non-
|
|
185
|
+
* array object, we treat it as an object key (workflow shapes mix arrays and
|
|
186
|
+
* objects fairly freely; we err on the side of preserving structure).
|
|
187
|
+
*
|
|
188
|
+
* Terminal-segment guard: refuses to overwrite an existing object with a
|
|
189
|
+
* non-object value. The LLM sometimes emits a paramPath that points at a
|
|
190
|
+
* parent scope rather than a leaf (e.g.
|
|
191
|
+
* `nodes["Hourly Trigger"].parameters` for a question whose answer is a
|
|
192
|
+
* channel name); naively writing the string there replaces the entire
|
|
193
|
+
* `parameters` object and the workflow runner then rejects the workflow with
|
|
194
|
+
* `parameters must be object`. Throwing here gives `applyResolutions` a
|
|
195
|
+
* chance to fall back to the userNotes path.
|
|
196
|
+
*/
|
|
197
|
+
export function setByDotPath(obj, paramPath, value) {
|
|
198
|
+
const segments = parseParamPath(paramPath);
|
|
199
|
+
let cur = obj;
|
|
200
|
+
for (let i = 0; i < segments.length - 1; i += 1) {
|
|
201
|
+
const seg = segments[i];
|
|
202
|
+
const isArrayIndex = /^[0-9]+$/.test(seg);
|
|
203
|
+
if (Array.isArray(cur)) {
|
|
204
|
+
let idx;
|
|
205
|
+
if (isArrayIndex) {
|
|
206
|
+
idx = Number(seg);
|
|
207
|
+
}
|
|
208
|
+
else {
|
|
209
|
+
idx = findArrayIndexByNameOrId(cur, seg);
|
|
210
|
+
if (idx < 0) {
|
|
211
|
+
throw new Error(`paramPath segment "${seg}" did not match any element by name/id at depth ${i}`);
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
let next = cur[idx];
|
|
215
|
+
if (next === undefined || next === null) {
|
|
216
|
+
next = /^[0-9]+$/.test(segments[i + 1]) ? [] : {};
|
|
217
|
+
cur[idx] = next;
|
|
218
|
+
}
|
|
219
|
+
if (typeof next !== 'object') {
|
|
220
|
+
throw new Error(`paramPath cannot descend into non-object at "${seg}" (depth ${i})`);
|
|
221
|
+
}
|
|
222
|
+
cur = next;
|
|
223
|
+
continue;
|
|
224
|
+
}
|
|
225
|
+
let next = cur[seg];
|
|
226
|
+
if (next === undefined || next === null) {
|
|
227
|
+
next = /^[0-9]+$/.test(segments[i + 1]) ? [] : {};
|
|
228
|
+
cur[seg] = next;
|
|
229
|
+
}
|
|
230
|
+
if (typeof next !== 'object') {
|
|
231
|
+
throw new Error(`paramPath cannot descend into non-object at "${seg}" (depth ${i})`);
|
|
232
|
+
}
|
|
233
|
+
cur = next;
|
|
234
|
+
}
|
|
235
|
+
const last = segments[segments.length - 1];
|
|
236
|
+
const isNonNullObject = (v) => v !== null && typeof v === 'object';
|
|
237
|
+
if (Array.isArray(cur)) {
|
|
238
|
+
let idx;
|
|
239
|
+
if (/^[0-9]+$/.test(last)) {
|
|
240
|
+
idx = Number(last);
|
|
241
|
+
}
|
|
242
|
+
else {
|
|
243
|
+
idx = findArrayIndexByNameOrId(cur, last);
|
|
244
|
+
if (idx < 0) {
|
|
245
|
+
throw new Error(`paramPath terminal segment "${last}" did not match any element by name/id at array`);
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
if (isNonNullObject(cur[idx]) && !isNonNullObject(value)) {
|
|
249
|
+
throw new Error(`paramPath terminal "${last}" currently holds an object; refusing to overwrite with non-object value (path likely points at a parent scope rather than a leaf field)`);
|
|
250
|
+
}
|
|
251
|
+
cur[idx] = value;
|
|
252
|
+
}
|
|
253
|
+
else {
|
|
254
|
+
const existing = cur[last];
|
|
255
|
+
if (isNonNullObject(existing) && !isNonNullObject(value)) {
|
|
256
|
+
throw new Error(`paramPath terminal "${last}" currently holds an object; refusing to overwrite with non-object value (path likely points at a parent scope rather than a leaf field)`);
|
|
257
|
+
}
|
|
258
|
+
cur[last] = value;
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
/**
|
|
262
|
+
* Append a free-form answer to `draft._meta.userNotes`. Used for
|
|
263
|
+
* clarifications with no `paramPath` AND as the fallback when
|
|
264
|
+
* `setByDotPath` can't resolve a paramPath against the current draft.
|
|
265
|
+
* Subsequent LLM regeneration rounds read these notes from `_meta` so the
|
|
266
|
+
* user's answer is preserved across the failure rather than discarded.
|
|
267
|
+
*/
|
|
268
|
+
function appendUserNote(draft, value) {
|
|
269
|
+
const existingMeta = draft._meta;
|
|
270
|
+
const meta = existingMeta && typeof existingMeta === 'object'
|
|
271
|
+
? existingMeta
|
|
272
|
+
: {};
|
|
273
|
+
draft._meta = meta;
|
|
274
|
+
let notes;
|
|
275
|
+
if (Array.isArray(meta.userNotes)) {
|
|
276
|
+
notes = meta.userNotes;
|
|
277
|
+
}
|
|
278
|
+
else {
|
|
279
|
+
notes = meta.userNotes !== null && meta.userNotes !== undefined ? [String(meta.userNotes)] : [];
|
|
280
|
+
meta.userNotes = notes;
|
|
281
|
+
}
|
|
282
|
+
notes.push(value);
|
|
283
|
+
}
|
|
284
|
+
export function applyResolutions(draft, resolutions) {
|
|
285
|
+
for (const r of resolutions) {
|
|
286
|
+
if (!r || typeof r.paramPath !== 'string') {
|
|
287
|
+
return { ok: false, error: 'resolution missing paramPath' };
|
|
288
|
+
}
|
|
289
|
+
if (typeof r.value !== 'string') {
|
|
290
|
+
return {
|
|
291
|
+
ok: false,
|
|
292
|
+
error: 'resolution value must be a string',
|
|
293
|
+
paramPath: r.paramPath,
|
|
294
|
+
};
|
|
295
|
+
}
|
|
296
|
+
if (r.paramPath.length === 0) {
|
|
297
|
+
// Free-form clarification with no field to wire into. Record the user's
|
|
298
|
+
// answer under draft._meta.userNotes so subsequent LLM iterations can
|
|
299
|
+
// consume the context, but don't mutate the workflow itself.
|
|
300
|
+
appendUserNote(draft, r.value);
|
|
301
|
+
continue;
|
|
302
|
+
}
|
|
303
|
+
// Surface structural parse errors (unterminated bracket, empty
|
|
304
|
+
// identifier, etc.) up to the caller as a 400 — these signal a
|
|
305
|
+
// malformed LLM emission and cannot be silently recovered into
|
|
306
|
+
// userNotes without losing the failure mode in the metrics pipeline.
|
|
307
|
+
try {
|
|
308
|
+
parseParamPath(r.paramPath);
|
|
309
|
+
}
|
|
310
|
+
catch (err) {
|
|
311
|
+
return {
|
|
312
|
+
ok: false,
|
|
313
|
+
error: `paramPath is structurally invalid: ${err instanceof Error ? err.message : String(err)}`,
|
|
314
|
+
paramPath: r.paramPath,
|
|
315
|
+
};
|
|
316
|
+
}
|
|
317
|
+
try {
|
|
318
|
+
setByDotPath(draft, r.paramPath, r.value);
|
|
319
|
+
}
|
|
320
|
+
catch (err) {
|
|
321
|
+
// Lookup-time failure: the path parsed cleanly but didn't resolve
|
|
322
|
+
// against the current draft (e.g. references a node the LLM didn't
|
|
323
|
+
// actually create, or points at a parent scope rather than a leaf
|
|
324
|
+
// field). Failing the whole resolution batch with a 400 is a
|
|
325
|
+
// dead-end — the user has no way to recover without re-prompting
|
|
326
|
+
// from scratch. Log a warn and record the answer as a free-form
|
|
327
|
+
// note so the next regeneration round can use it.
|
|
328
|
+
const errMsg = err instanceof Error ? err.message : String(err);
|
|
329
|
+
logger.warn({
|
|
330
|
+
src: 'plugin:workflow:clarification:applyResolutions',
|
|
331
|
+
err: errMsg,
|
|
332
|
+
paramPath: r.paramPath,
|
|
333
|
+
}, `setByDotPath failed for paramPath "${r.paramPath}"; recording "${r.value}" as a free-form note instead`);
|
|
334
|
+
appendUserNote(draft, r.value);
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
return { ok: true };
|
|
338
|
+
}
|
|
339
|
+
/**
|
|
340
|
+
* Drop the resolved clarifications from the draft's `_meta` so the next
|
|
341
|
+
* read of the draft does not re-prompt the user for the same parameter.
|
|
342
|
+
*
|
|
343
|
+
* Two pruning paths:
|
|
344
|
+
* 1. Object-form clarifications with paramPath → prune by paramPath match.
|
|
345
|
+
* 2. String-form clarifications (LLM emits free-form questions without a
|
|
346
|
+
* paramPath) and object-form clarifications with empty paramPath →
|
|
347
|
+
* prune positionally by `freeFormCount` (UI presents them in order, so
|
|
348
|
+
* each free-form resolution consumes the next one).
|
|
349
|
+
*
|
|
350
|
+
* Positional-pruning contract: free-form items are dropped from the head of
|
|
351
|
+
* the stored list in order. The UI must therefore submit answers in the
|
|
352
|
+
* order they were presented, with no skipped or out-of-order items in a
|
|
353
|
+
* single batch — otherwise the wrong question gets pruned. If we ever need
|
|
354
|
+
* to support partial/interleaved submissions, switch the resolution payload
|
|
355
|
+
* to send the answered question text and match by value here instead.
|
|
356
|
+
*/
|
|
357
|
+
export function pruneResolvedClarifications(draft, resolved, freeFormCount = 0) {
|
|
358
|
+
const meta = draft._meta;
|
|
359
|
+
if (!meta || typeof meta !== 'object') {
|
|
360
|
+
return;
|
|
361
|
+
}
|
|
362
|
+
const list = meta.requiresClarification;
|
|
363
|
+
if (!Array.isArray(list)) {
|
|
364
|
+
return;
|
|
365
|
+
}
|
|
366
|
+
let toDropFreeForm = freeFormCount;
|
|
367
|
+
const remaining = list.filter((item) => {
|
|
368
|
+
if (typeof item === 'string') {
|
|
369
|
+
if (toDropFreeForm > 0) {
|
|
370
|
+
toDropFreeForm -= 1;
|
|
371
|
+
return false;
|
|
372
|
+
}
|
|
373
|
+
return true;
|
|
374
|
+
}
|
|
375
|
+
if (item && typeof item === 'object') {
|
|
376
|
+
const path = item.paramPath;
|
|
377
|
+
if (typeof path === 'string' && path.length > 0 && resolved.has(path)) {
|
|
378
|
+
return false;
|
|
379
|
+
}
|
|
380
|
+
// Empty-paramPath object-form: also positional.
|
|
381
|
+
if ((typeof path !== 'string' || path.length === 0) && toDropFreeForm > 0) {
|
|
382
|
+
toDropFreeForm -= 1;
|
|
383
|
+
return false;
|
|
384
|
+
}
|
|
385
|
+
}
|
|
386
|
+
return true;
|
|
387
|
+
});
|
|
388
|
+
if (remaining.length === 0) {
|
|
389
|
+
delete meta.requiresClarification;
|
|
390
|
+
}
|
|
391
|
+
else {
|
|
392
|
+
meta.requiresClarification = remaining;
|
|
393
|
+
}
|
|
394
|
+
}
|
|
395
|
+
/**
|
|
396
|
+
* Build a catalog snapshot for the platforms referenced by `clarifications`.
|
|
397
|
+
* If multiple clarifications reference the same platform, we union their
|
|
398
|
+
* groupId scopes — broader queries (no scope) always win.
|
|
399
|
+
*/
|
|
400
|
+
export async function buildCatalogSnapshot(catalog, clarifications) {
|
|
401
|
+
const platforms = new Set();
|
|
402
|
+
for (const c of clarifications) {
|
|
403
|
+
if (c.platform) {
|
|
404
|
+
platforms.add(c.platform);
|
|
405
|
+
}
|
|
406
|
+
}
|
|
407
|
+
if (platforms.size === 0) {
|
|
408
|
+
return [];
|
|
409
|
+
}
|
|
410
|
+
const out = [];
|
|
411
|
+
const seen = new Set();
|
|
412
|
+
for (const platform of platforms) {
|
|
413
|
+
const groups = await catalog.listGroups({ platform });
|
|
414
|
+
for (const g of groups) {
|
|
415
|
+
const key = `${g.platform}::${g.groupId}`;
|
|
416
|
+
if (seen.has(key)) {
|
|
417
|
+
continue;
|
|
418
|
+
}
|
|
419
|
+
seen.add(key);
|
|
420
|
+
out.push(g);
|
|
421
|
+
}
|
|
422
|
+
}
|
|
423
|
+
return out;
|
|
424
|
+
}
|
|
425
|
+
//# sourceMappingURL=workflow-clarification.js.map
|