forge-openclaw-plugin 0.2.25 → 0.2.27
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 +59 -3
- package/dist/assets/{board-VmF4FAfr.js → board-C6jCchjI.js} +3 -3
- package/dist/assets/{board-VmF4FAfr.js.map → board-C6jCchjI.js.map} +1 -1
- package/dist/assets/index-DVvS8iiU.css +1 -0
- package/dist/assets/index-zYB-9Dfo.js +85 -0
- package/dist/assets/index-zYB-9Dfo.js.map +1 -0
- package/dist/assets/knowledge-graph-layout.worker-DRvzPxhP.js +2 -0
- package/dist/assets/knowledge-graph-layout.worker-DRvzPxhP.js.map +1 -0
- package/dist/assets/{motion-DvkU14p-.js → motion-DFHrH2rd.js} +2 -2
- package/dist/assets/{motion-DvkU14p-.js.map → motion-DFHrH2rd.js.map} +1 -1
- package/dist/assets/{table-DgiPof9E.js → table-ZL7Di_u3.js} +2 -2
- package/dist/assets/{table-DgiPof9E.js.map → table-ZL7Di_u3.js.map} +1 -1
- package/dist/assets/{ui-nYfoC0Gq.js → ui-CKNPpz7q.js} +2 -2
- package/dist/assets/{ui-nYfoC0Gq.js.map → ui-CKNPpz7q.js.map} +1 -1
- package/dist/assets/vendor-DoNZuFhn.js +1247 -0
- package/dist/assets/vendor-DoNZuFhn.js.map +1 -0
- package/dist/index.html +7 -8
- package/dist/openclaw/local-runtime.d.ts +3 -1
- package/dist/openclaw/local-runtime.js +67 -15
- package/dist/openclaw/plugin-entry-shared.js +24 -2
- package/dist/openclaw/plugin-sdk-types.d.ts +17 -0
- package/dist/openclaw/routes.d.ts +27 -0
- package/dist/openclaw/routes.js +16 -12
- package/dist/openclaw/tools.js +0 -3
- package/dist/server/server/migrations/001_core.sql +411 -0
- package/dist/server/server/migrations/002_psyche.sql +392 -0
- package/dist/server/server/migrations/003_habits.sql +30 -0
- package/dist/server/server/migrations/004_habit_links.sql +8 -0
- package/dist/server/server/migrations/005_habit_psyche_links.sql +24 -0
- package/dist/server/server/migrations/006_work_adjustments.sql +14 -0
- package/dist/server/server/migrations/007_weekly_review_closures.sql +17 -0
- package/dist/server/server/migrations/008_calendar_execution.sql +147 -0
- package/dist/server/server/migrations/009_true_calendar_events.sql +195 -0
- package/dist/server/server/migrations/010_calendar_selection_state.sql +6 -0
- package/dist/server/server/migrations/011_calendar_timezone_backfill.sql +11 -0
- package/dist/server/server/migrations/012_work_block_ranges.sql +7 -0
- package/dist/server/server/migrations/013_microsoft_local_auth_settings.sql +8 -0
- package/dist/server/server/migrations/014_note_tags_and_ephemeral.sql +8 -0
- package/dist/server/server/migrations/015_multi_user_and_strategies.sql +244 -0
- package/dist/server/server/migrations/016_health_companion.sql +158 -0
- package/dist/server/server/migrations/016_strategy_contracts_and_user_graph.sql +22 -0
- package/dist/server/server/migrations/017_preferences.sql +131 -0
- package/dist/server/server/migrations/018_preference_catalogs.sql +31 -0
- package/dist/server/server/migrations/019_wiki_memory.sql +255 -0
- package/dist/server/server/migrations/020_wiki_page_hierarchy.sql +11 -0
- package/dist/server/server/migrations/021_hide_evidence_from_wiki_index.sql +3 -0
- package/dist/server/server/migrations/022_wiki_ingest_background.sql +85 -0
- package/dist/server/server/migrations/023_diagnostic_logs.sql +28 -0
- package/dist/server/server/migrations/024_questionnaires.sql +96 -0
- package/dist/server/server/migrations/025_ai_model_connections.sql +26 -0
- package/dist/server/server/migrations/026_custom_theme_settings.sql +2 -0
- package/dist/server/server/migrations/027_ai_processors.sql +31 -0
- package/dist/server/server/migrations/028_movement_domain.sql +136 -0
- package/dist/server/server/migrations/029_watch_micro_capture.sql +23 -0
- package/dist/server/server/migrations/030_surface_layouts.sql +5 -0
- package/dist/server/server/migrations/031_ai_processor_runtime_upgrades.sql +10 -0
- package/dist/server/server/migrations/032_ai_connectors.sql +44 -0
- package/dist/server/server/migrations/033_movement_trip_point_sync.sql +36 -0
- package/dist/server/server/migrations/034_movement_segment_sync.sql +49 -0
- package/dist/server/server/migrations/035_google_local_auth_settings.sql +2 -0
- package/dist/server/server/migrations/036_google_local_auth_client_secret.sql +2 -0
- package/dist/server/server/migrations/037_workbench_public_inputs_and_run_inputs.sql +5 -0
- package/dist/server/server/migrations/038_data_management_settings.sql +11 -0
- package/dist/server/server/migrations/039_life_force_and_action_points.sql +114 -0
- package/dist/server/server/migrations/040_screen_time_domain.sql +89 -0
- package/dist/server/server/migrations/041_companion_source_states.sql +21 -0
- package/dist/server/server/migrations/042_movement_boxes.sql +47 -0
- package/dist/server/server/migrations/043_movement_box_overlap_overrides.sql +26 -0
- package/dist/server/{app.js → server/src/app.js} +2112 -414
- package/dist/server/server/src/connectors/box-registry.js +223 -0
- package/dist/server/server/src/data-management-types.js +107 -0
- package/dist/server/{db.js → server/src/db.js} +72 -4
- package/dist/server/server/src/debug.js +19 -0
- package/dist/server/{demo-data.js → server/src/demo-data.js} +2 -2
- package/dist/server/{health.js → server/src/health.js} +702 -18
- package/dist/server/{managers → server/src/managers}/platform/llm-manager.js +7 -4
- package/dist/server/server/src/managers/platform/mock-workbench-provider.js +149 -0
- package/dist/server/{managers → server/src/managers}/platform/secrets-manager.js +18 -1
- package/dist/server/{managers → server/src/managers}/runtime.js +9 -0
- package/dist/server/{movement.js → server/src/movement.js} +1971 -112
- package/dist/server/{openapi.js → server/src/openapi.js} +491 -3
- package/dist/server/{psyche-types.js → server/src/psyche-types.js} +9 -1
- package/dist/server/{repositories → server/src/repositories}/activity-events.js +8 -0
- package/dist/server/{repositories → server/src/repositories}/ai-connectors.js +758 -47
- package/dist/server/{repositories → server/src/repositories}/calendar.js +1 -1
- package/dist/server/{repositories → server/src/repositories}/habits.js +37 -1
- package/dist/server/{repositories → server/src/repositories}/model-settings.js +13 -3
- package/dist/server/{repositories → server/src/repositories}/notes.js +3 -0
- package/dist/server/{repositories → server/src/repositories}/settings.js +431 -21
- package/dist/server/{repositories → server/src/repositories}/tasks.js +170 -10
- package/dist/server/server/src/runtime-data-root.js +82 -0
- package/dist/server/server/src/screen-time.js +802 -0
- package/dist/server/{services → server/src/services}/calendar-runtime.js +775 -58
- package/dist/server/server/src/services/data-management.js +788 -0
- package/dist/server/{services → server/src/services}/entity-crud.js +205 -2
- package/dist/server/server/src/services/google-calendar-oauth-config.js +176 -0
- package/dist/server/server/src/services/knowledge-graph.js +1455 -0
- package/dist/server/server/src/services/life-force-model.js +197 -0
- package/dist/server/server/src/services/life-force.js +1270 -0
- package/dist/server/server/src/services/psyche-observation-calendar.js +413 -0
- package/dist/server/{types.js → server/src/types.js} +420 -29
- package/dist/server/server/src/web.js +332 -0
- package/dist/server/src/components/customization/utility-widgets.js +439 -0
- package/dist/server/src/components/ui/info-tooltip.js +25 -0
- package/dist/server/src/components/workbench-boxes/calendar/calendar-boxes.js +78 -0
- package/dist/server/src/components/workbench-boxes/goals/goals-boxes.js +62 -0
- package/dist/server/src/components/workbench-boxes/habits/habits-boxes.js +62 -0
- package/dist/server/src/components/workbench-boxes/health/health-boxes.js +147 -0
- package/dist/server/src/components/workbench-boxes/insights/insights-boxes.js +50 -0
- package/dist/server/src/components/workbench-boxes/kanban/kanban-boxes.js +136 -0
- package/dist/server/src/components/workbench-boxes/movement/movement-boxes.js +47 -0
- package/dist/server/src/components/workbench-boxes/notes/notes-boxes.js +132 -0
- package/dist/server/src/components/workbench-boxes/overview/overview-boxes.js +65 -0
- package/dist/server/src/components/workbench-boxes/preferences/preferences-boxes.js +78 -0
- package/dist/server/src/components/workbench-boxes/projects/projects-boxes.js +62 -0
- package/dist/server/src/components/workbench-boxes/psyche/psyche-boxes.js +88 -0
- package/dist/server/src/components/workbench-boxes/questionnaires/questionnaires-boxes.js +61 -0
- package/dist/server/src/components/workbench-boxes/review/review-boxes.js +53 -0
- package/dist/server/src/components/workbench-boxes/shared/define-workbench-box.js +6 -0
- package/dist/server/src/components/workbench-boxes/shared/generic-node-view.js +49 -0
- package/dist/server/src/components/workbench-boxes/strategies/strategies-boxes.js +62 -0
- package/dist/server/src/components/workbench-boxes/tasks/tasks-boxes.js +76 -0
- package/dist/server/src/components/workbench-boxes/today/today-boxes.js +78 -0
- package/dist/server/src/components/workbench-boxes/wiki/wiki-boxes.js +60 -0
- package/dist/server/src/lib/api-error.js +37 -0
- package/dist/server/src/lib/api.js +2118 -0
- package/dist/server/src/lib/calendar-name-deduper.js +144 -0
- package/dist/server/src/lib/data-management-types.js +1 -0
- package/dist/server/src/lib/diagnostics.js +67 -0
- package/dist/server/src/lib/entity-visuals.js +279 -0
- package/dist/server/src/lib/knowledge-graph-types.js +276 -0
- package/dist/server/src/lib/knowledge-graph.js +470 -0
- package/dist/server/src/lib/psyche-types.js +1 -0
- package/dist/server/src/lib/questionnaire-types.js +1 -0
- package/dist/server/src/lib/runtime-paths.js +24 -0
- package/dist/server/src/lib/schemas.js +238 -0
- package/dist/server/src/lib/snapshot-normalizer.js +416 -0
- package/dist/server/src/lib/theme-system.js +319 -0
- package/dist/server/src/lib/types.js +1 -0
- package/dist/server/src/lib/utils.js +22 -0
- package/dist/server/src/lib/workbench/boxes.js +16 -0
- package/dist/server/src/lib/workbench/contracts.js +229 -0
- package/dist/server/src/lib/workbench/nodes.js +215 -0
- package/dist/server/src/lib/workbench/registry.js +120 -0
- package/dist/server/src/lib/workbench/runtime.js +397 -0
- package/dist/server/src/lib/workbench/tool-catalog.js +68 -0
- package/openclaw.plugin.json +1 -1
- package/package.json +1 -1
- package/server/index.js +68 -0
- package/server/migrations/035_google_local_auth_settings.sql +2 -0
- package/server/migrations/036_google_local_auth_client_secret.sql +2 -0
- package/server/migrations/037_workbench_public_inputs_and_run_inputs.sql +5 -0
- package/server/migrations/038_data_management_settings.sql +11 -0
- package/server/migrations/039_life_force_and_action_points.sql +114 -0
- package/server/migrations/040_screen_time_domain.sql +89 -0
- package/server/migrations/041_companion_source_states.sql +21 -0
- package/server/migrations/042_movement_boxes.sql +47 -0
- package/server/migrations/043_movement_box_overlap_overrides.sql +26 -0
- package/skills/forge-openclaw/SKILL.md +27 -11
- package/skills/forge-openclaw/entity_conversation_playbooks.md +411 -46
- package/skills/forge-openclaw/psyche_entity_playbooks.md +195 -20
- package/dist/assets/index-CFCKDIMH.js +0 -67
- package/dist/assets/index-CFCKDIMH.js.map +0 -1
- package/dist/assets/index-ZPY6U1TU.css +0 -1
- package/dist/assets/vendor-D9PTEPSB.js +0 -824
- package/dist/assets/vendor-D9PTEPSB.js.map +0 -1
- package/dist/assets/viz-Cqb6s--o.js +0 -34
- package/dist/assets/viz-Cqb6s--o.js.map +0 -1
- package/dist/server/connectors/box-registry.js +0 -257
- package/dist/server/services/psyche-observation-calendar.js +0 -46
- package/dist/server/web.js +0 -98
- /package/dist/server/{discovery-advertiser.js → server/src/discovery-advertiser.js} +0 -0
- /package/dist/server/{e2e-server.js → server/src/e2e-server.js} +0 -0
- /package/dist/server/{errors.js → server/src/errors.js} +0 -0
- /package/dist/server/{index.js → server/src/index.js} +0 -0
- /package/dist/server/{managers → server/src/managers}/base.js +0 -0
- /package/dist/server/{managers → server/src/managers}/contracts.js +0 -0
- /package/dist/server/{managers → server/src/managers}/platform/api-gateway-manager.js +0 -0
- /package/dist/server/{managers → server/src/managers}/platform/audit-manager.js +0 -0
- /package/dist/server/{managers → server/src/managers}/platform/authentication-manager.js +0 -0
- /package/dist/server/{managers → server/src/managers}/platform/authorization-manager.js +0 -0
- /package/dist/server/{managers → server/src/managers}/platform/background-job-manager.js +0 -0
- /package/dist/server/{managers → server/src/managers}/platform/configuration-manager.js +0 -0
- /package/dist/server/{managers → server/src/managers}/platform/database-manager.js +0 -0
- /package/dist/server/{managers → server/src/managers}/platform/event-bus-manager.js +0 -0
- /package/dist/server/{managers → server/src/managers}/platform/external-service-manager.js +0 -0
- /package/dist/server/{managers → server/src/managers}/platform/health-manager.js +0 -0
- /package/dist/server/{managers → server/src/managers}/platform/migration-manager.js +0 -0
- /package/dist/server/{managers → server/src/managers}/platform/openai-responses-provider.js +0 -0
- /package/dist/server/{managers → server/src/managers}/platform/search-index-manager.js +0 -0
- /package/dist/server/{managers → server/src/managers}/platform/session-manager.js +0 -0
- /package/dist/server/{managers → server/src/managers}/platform/storage-manager.js +0 -0
- /package/dist/server/{managers → server/src/managers}/platform/token-manager.js +0 -0
- /package/dist/server/{managers → server/src/managers}/platform/transaction-manager.js +0 -0
- /package/dist/server/{managers → server/src/managers}/platform/trusted-network.js +0 -0
- /package/dist/server/{managers → server/src/managers}/type-guards.js +0 -0
- /package/dist/server/{preferences-seeds.js → server/src/preferences-seeds.js} +0 -0
- /package/dist/server/{preferences-types.js → server/src/preferences-types.js} +0 -0
- /package/dist/server/{questionnaire-flow.js → server/src/questionnaire-flow.js} +0 -0
- /package/dist/server/{questionnaire-seeds.js → server/src/questionnaire-seeds.js} +0 -0
- /package/dist/server/{questionnaire-types.js → server/src/questionnaire-types.js} +0 -0
- /package/dist/server/{repositories → server/src/repositories}/ai-processors.js +0 -0
- /package/dist/server/{repositories → server/src/repositories}/collaboration.js +0 -0
- /package/dist/server/{repositories → server/src/repositories}/deleted-entities.js +0 -0
- /package/dist/server/{repositories → server/src/repositories}/diagnostic-logs.js +0 -0
- /package/dist/server/{repositories → server/src/repositories}/domains.js +0 -0
- /package/dist/server/{repositories → server/src/repositories}/entity-ownership.js +0 -0
- /package/dist/server/{repositories → server/src/repositories}/event-log.js +0 -0
- /package/dist/server/{repositories → server/src/repositories}/goals.js +0 -0
- /package/dist/server/{repositories → server/src/repositories}/preferences.js +0 -0
- /package/dist/server/{repositories → server/src/repositories}/projects.js +0 -0
- /package/dist/server/{repositories → server/src/repositories}/psyche.js +0 -0
- /package/dist/server/{repositories → server/src/repositories}/questionnaires.js +0 -0
- /package/dist/server/{repositories → server/src/repositories}/rewards.js +0 -0
- /package/dist/server/{repositories → server/src/repositories}/strategies.js +0 -0
- /package/dist/server/{repositories → server/src/repositories}/surface-layouts.js +0 -0
- /package/dist/server/{repositories → server/src/repositories}/tags.js +0 -0
- /package/dist/server/{repositories → server/src/repositories}/task-runs.js +0 -0
- /package/dist/server/{repositories → server/src/repositories}/users.js +0 -0
- /package/dist/server/{repositories → server/src/repositories}/weekly-reviews.js +0 -0
- /package/dist/server/{repositories → server/src/repositories}/wiki-memory.js +0 -0
- /package/dist/server/{repositories → server/src/repositories}/work-adjustments.js +0 -0
- /package/dist/server/{seed-demo.js → server/src/seed-demo.js} +0 -0
- /package/dist/server/{services → server/src/services}/context.js +0 -0
- /package/dist/server/{services → server/src/services}/dashboard.js +0 -0
- /package/dist/server/{services → server/src/services}/gamification.js +0 -0
- /package/dist/server/{services → server/src/services}/insights.js +0 -0
- /package/dist/server/{services → server/src/services}/openai-codex-oauth.js +0 -0
- /package/dist/server/{services → server/src/services}/projects.js +0 -0
- /package/dist/server/{services → server/src/services}/psyche.js +0 -0
- /package/dist/server/{services → server/src/services}/relations.js +0 -0
- /package/dist/server/{services → server/src/services}/reviews.js +0 -0
- /package/dist/server/{services → server/src/services}/run-recovery.js +0 -0
- /package/dist/server/{services → server/src/services}/tagging.js +0 -0
- /package/dist/server/{services → server/src/services}/task-run-watchdog.js +0 -0
- /package/dist/server/{services → server/src/services}/work-time.js +0 -0
- /package/dist/server/{watch-mobile.js → server/src/watch-mobile.js} +0 -0
|
@@ -8,6 +8,7 @@ import { createAiConnectorSchema, aiConnectorConversationSchema, aiConnectorRunR
|
|
|
8
8
|
import { FORGE_DEFAULT_AGENT_ID, getAiModelConnectionById, listAiModelConnections, readModelConnectionCredential } from "./model-settings.js";
|
|
9
9
|
import { getAiProcessorById, listAiProcessorLinks, listAiProcessors } from "./ai-processors.js";
|
|
10
10
|
import { buildConnectorOutputCatalogEntry, executeForgeBoxTool, resolveForgeBoxSnapshot } from "../connectors/box-registry.js";
|
|
11
|
+
import { normalizeWorkbenchPortDefinition } from "../../../src/lib/workbench/nodes.js";
|
|
11
12
|
const execFile = promisify(execFileCallback);
|
|
12
13
|
const MAX_TOOL_STEPS = 6;
|
|
13
14
|
const MAX_RUN_HISTORY = 20;
|
|
@@ -164,7 +165,7 @@ function buildDefaultGraph(kind, title) {
|
|
|
164
165
|
data: {
|
|
165
166
|
label: "Output",
|
|
166
167
|
description: "Published connector output.",
|
|
167
|
-
outputKey: "
|
|
168
|
+
outputKey: "answer",
|
|
168
169
|
enabledToolKeys: []
|
|
169
170
|
}
|
|
170
171
|
}
|
|
@@ -190,20 +191,20 @@ function ensurePublishedOutputs(connectorId, graph) {
|
|
|
190
191
|
buildConnectorOutputCatalogEntry({
|
|
191
192
|
connectorId,
|
|
192
193
|
title: "Connector",
|
|
193
|
-
outputId: "
|
|
194
|
+
outputId: "answer"
|
|
194
195
|
})
|
|
195
196
|
].map((entry) => ({
|
|
196
|
-
id: entry.
|
|
197
|
+
id: entry.id.replace(/^connector-output:/, ""),
|
|
197
198
|
nodeId: "node_output",
|
|
198
|
-
label: entry.
|
|
199
|
-
apiPath: `/api/v1/
|
|
199
|
+
label: entry.title,
|
|
200
|
+
apiPath: `/api/v1/workbench/flows/${connectorId}/output`
|
|
200
201
|
}));
|
|
201
202
|
}
|
|
202
203
|
return outputNodes.map((node, index) => ({
|
|
203
204
|
id: `${connectorId}_out_${index + 1}`,
|
|
204
205
|
nodeId: node.id,
|
|
205
206
|
label: node.data.label || `Output ${index + 1}`,
|
|
206
|
-
apiPath: `/api/v1/
|
|
207
|
+
apiPath: `/api/v1/workbench/flows/${connectorId}/output`
|
|
207
208
|
}));
|
|
208
209
|
}
|
|
209
210
|
function mapRun(row) {
|
|
@@ -213,6 +214,7 @@ function mapRun(row) {
|
|
|
213
214
|
mode: row.mode,
|
|
214
215
|
status: row.status,
|
|
215
216
|
userInput: row.user_input,
|
|
217
|
+
inputs: parseJson(row.inputs_json, {}),
|
|
216
218
|
context: parseJson(row.context_json, {}),
|
|
217
219
|
conversationId: row.conversation_id,
|
|
218
220
|
result: parseJson(row.result_json, null),
|
|
@@ -233,6 +235,8 @@ function mapConversation(row) {
|
|
|
233
235
|
});
|
|
234
236
|
}
|
|
235
237
|
function mapConnector(row) {
|
|
238
|
+
const rawGraph = parseJson(row.graph_json, { nodes: [], edges: [] });
|
|
239
|
+
const normalizedGraph = normalizeConnectorGraph(rawGraph);
|
|
236
240
|
return aiConnectorSchema.parse({
|
|
237
241
|
id: row.id,
|
|
238
242
|
slug: row.slug,
|
|
@@ -241,7 +245,8 @@ function mapConnector(row) {
|
|
|
241
245
|
kind: row.kind,
|
|
242
246
|
homeSurfaceId: row.home_surface_id,
|
|
243
247
|
endpointEnabled: row.endpoint_enabled === 1,
|
|
244
|
-
graph:
|
|
248
|
+
graph: normalizedGraph,
|
|
249
|
+
publicInputs: parseJson(row.public_inputs_json, []),
|
|
245
250
|
publishedOutputs: parseJson(row.published_outputs_json, []),
|
|
246
251
|
lastRun: parseJson(row.last_run_json, null),
|
|
247
252
|
legacyProcessorId: row.legacy_processor_id,
|
|
@@ -255,6 +260,37 @@ export function listAiConnectorRuns(connectorId) {
|
|
|
255
260
|
.all(connectorId, MAX_RUN_HISTORY);
|
|
256
261
|
return rows.map(mapRun);
|
|
257
262
|
}
|
|
263
|
+
export function getAiConnectorRunById(connectorId, runId) {
|
|
264
|
+
const row = getDatabase()
|
|
265
|
+
.prepare(`SELECT * FROM ai_connector_runs WHERE connector_id = ? AND id = ?`)
|
|
266
|
+
.get(connectorId, runId);
|
|
267
|
+
return row ? mapRun(row) : null;
|
|
268
|
+
}
|
|
269
|
+
export function getAiConnectorRunNodeResults(connectorId, runId) {
|
|
270
|
+
const run = getAiConnectorRunById(connectorId, runId);
|
|
271
|
+
return run?.result?.nodeResults ?? null;
|
|
272
|
+
}
|
|
273
|
+
export function getAiConnectorRunNodeResult(connectorId, runId, nodeId) {
|
|
274
|
+
const results = getAiConnectorRunNodeResults(connectorId, runId);
|
|
275
|
+
if (!results) {
|
|
276
|
+
return null;
|
|
277
|
+
}
|
|
278
|
+
return results.find((entry) => entry.nodeId === nodeId) ?? null;
|
|
279
|
+
}
|
|
280
|
+
export function getLatestAiConnectorNodeOutput(connectorId, nodeId) {
|
|
281
|
+
const run = listAiConnectorRuns(connectorId).find((entry) => entry.status === "completed" && entry.result);
|
|
282
|
+
if (!run?.result) {
|
|
283
|
+
return null;
|
|
284
|
+
}
|
|
285
|
+
const nodeResult = run.result.nodeResults.find((entry) => entry.nodeId === nodeId) ?? null;
|
|
286
|
+
if (!nodeResult) {
|
|
287
|
+
return null;
|
|
288
|
+
}
|
|
289
|
+
return {
|
|
290
|
+
run,
|
|
291
|
+
nodeResult
|
|
292
|
+
};
|
|
293
|
+
}
|
|
258
294
|
export function getAiConnectorConversationById(conversationId) {
|
|
259
295
|
const row = getDatabase()
|
|
260
296
|
.prepare(`SELECT * FROM ai_connector_conversations WHERE id = ?`)
|
|
@@ -288,20 +324,21 @@ function updateConnectorLastRun(connectorId, run) {
|
|
|
288
324
|
function insertRun(input) {
|
|
289
325
|
getDatabase()
|
|
290
326
|
.prepare(`INSERT INTO ai_connector_runs (
|
|
291
|
-
id, connector_id, mode, status, user_input, context_json, conversation_id, result_json, error, created_at, completed_at
|
|
292
|
-
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
327
|
+
id, connector_id, mode, status, user_input, inputs_json, context_json, conversation_id, result_json, error, created_at, completed_at
|
|
328
|
+
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
293
329
|
ON CONFLICT(id) DO UPDATE SET
|
|
294
330
|
connector_id = excluded.connector_id,
|
|
295
331
|
mode = excluded.mode,
|
|
296
332
|
status = excluded.status,
|
|
297
333
|
user_input = excluded.user_input,
|
|
334
|
+
inputs_json = excluded.inputs_json,
|
|
298
335
|
context_json = excluded.context_json,
|
|
299
336
|
conversation_id = excluded.conversation_id,
|
|
300
337
|
result_json = excluded.result_json,
|
|
301
338
|
error = excluded.error,
|
|
302
339
|
created_at = excluded.created_at,
|
|
303
340
|
completed_at = excluded.completed_at`)
|
|
304
|
-
.run(input.id, input.connectorId, input.mode, input.status, input.userInput, JSON.stringify(input.context), input.conversationId, input.result ? JSON.stringify(input.result) : null, input.error, input.createdAt, input.completedAt);
|
|
341
|
+
.run(input.id, input.connectorId, input.mode, input.status, input.userInput, JSON.stringify(input.inputs), JSON.stringify(input.context), input.conversationId, input.result ? JSON.stringify(input.result) : null, input.error, input.createdAt, input.completedAt);
|
|
305
342
|
updateConnectorLastRun(input.connectorId, input);
|
|
306
343
|
return input;
|
|
307
344
|
}
|
|
@@ -316,11 +353,313 @@ function resolveAllowedPath(inputPath) {
|
|
|
316
353
|
}
|
|
317
354
|
function tryParseStructuredAgentResponse(value) {
|
|
318
355
|
try {
|
|
319
|
-
|
|
356
|
+
const parsed = JSON.parse(value);
|
|
357
|
+
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
358
|
+
return null;
|
|
359
|
+
}
|
|
360
|
+
const action = parsed.action;
|
|
361
|
+
if (action === "final") {
|
|
362
|
+
const text = parsed.text;
|
|
363
|
+
return {
|
|
364
|
+
action,
|
|
365
|
+
text: typeof text === "string" ? text : value
|
|
366
|
+
};
|
|
367
|
+
}
|
|
368
|
+
if (action === "tool") {
|
|
369
|
+
const tool = parsed.tool;
|
|
370
|
+
const args = parsed.args;
|
|
371
|
+
if (typeof tool !== "string" || tool.trim().length === 0) {
|
|
372
|
+
return null;
|
|
373
|
+
}
|
|
374
|
+
return {
|
|
375
|
+
action,
|
|
376
|
+
tool,
|
|
377
|
+
args: args && typeof args === "object" && !Array.isArray(args)
|
|
378
|
+
? args
|
|
379
|
+
: {}
|
|
380
|
+
};
|
|
381
|
+
}
|
|
382
|
+
return null;
|
|
383
|
+
}
|
|
384
|
+
catch {
|
|
385
|
+
return null;
|
|
386
|
+
}
|
|
387
|
+
}
|
|
388
|
+
function tryParseJsonObject(value) {
|
|
389
|
+
try {
|
|
390
|
+
const parsed = JSON.parse(value);
|
|
391
|
+
if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
|
|
392
|
+
return parsed;
|
|
393
|
+
}
|
|
394
|
+
return null;
|
|
395
|
+
}
|
|
396
|
+
catch {
|
|
397
|
+
return null;
|
|
398
|
+
}
|
|
399
|
+
}
|
|
400
|
+
function coerceText(value) {
|
|
401
|
+
if (typeof value === "string") {
|
|
402
|
+
return value;
|
|
403
|
+
}
|
|
404
|
+
if (value == null) {
|
|
405
|
+
return "";
|
|
406
|
+
}
|
|
407
|
+
if (typeof value === "number" || typeof value === "boolean") {
|
|
408
|
+
return String(value);
|
|
409
|
+
}
|
|
410
|
+
try {
|
|
411
|
+
return JSON.stringify(value);
|
|
320
412
|
}
|
|
321
413
|
catch {
|
|
414
|
+
return "";
|
|
415
|
+
}
|
|
416
|
+
}
|
|
417
|
+
function coerceJsonObject(value) {
|
|
418
|
+
return value && typeof value === "object" && !Array.isArray(value)
|
|
419
|
+
? value
|
|
420
|
+
: null;
|
|
421
|
+
}
|
|
422
|
+
function validatePortValueType(port, value) {
|
|
423
|
+
switch (port.kind) {
|
|
424
|
+
case "text":
|
|
425
|
+
case "markdown":
|
|
426
|
+
case "summary":
|
|
427
|
+
return typeof value === "string";
|
|
428
|
+
case "number":
|
|
429
|
+
return typeof value === "number" && Number.isFinite(value);
|
|
430
|
+
case "boolean":
|
|
431
|
+
return typeof value === "boolean";
|
|
432
|
+
case "array":
|
|
433
|
+
case "entity_list":
|
|
434
|
+
case "record_list":
|
|
435
|
+
return Array.isArray(value);
|
|
436
|
+
case "object":
|
|
437
|
+
case "json":
|
|
438
|
+
case "record":
|
|
439
|
+
case "context":
|
|
440
|
+
case "filters":
|
|
441
|
+
case "metrics":
|
|
442
|
+
case "timeline":
|
|
443
|
+
case "selection":
|
|
444
|
+
case "entity":
|
|
445
|
+
return Boolean(value) && typeof value === "object";
|
|
446
|
+
default:
|
|
447
|
+
return true;
|
|
448
|
+
}
|
|
449
|
+
}
|
|
450
|
+
function normalizePublicInputBindings(connector, publicInput) {
|
|
451
|
+
if (publicInput.bindings.length > 0) {
|
|
452
|
+
return publicInput.bindings;
|
|
453
|
+
}
|
|
454
|
+
return connector.graph.nodes.flatMap((node) => {
|
|
455
|
+
const inputs = defaultInputsForNode(node);
|
|
456
|
+
const params = node.data.params ?? [];
|
|
457
|
+
const matches = [];
|
|
458
|
+
if (inputs.some((entry) => entry.key === publicInput.key)) {
|
|
459
|
+
matches.push({
|
|
460
|
+
nodeId: node.id,
|
|
461
|
+
targetKey: publicInput.key,
|
|
462
|
+
targetKind: "input"
|
|
463
|
+
});
|
|
464
|
+
}
|
|
465
|
+
if (params.some((entry) => entry.key === publicInput.key)) {
|
|
466
|
+
matches.push({
|
|
467
|
+
nodeId: node.id,
|
|
468
|
+
targetKey: publicInput.key,
|
|
469
|
+
targetKind: "param"
|
|
470
|
+
});
|
|
471
|
+
}
|
|
472
|
+
return matches;
|
|
473
|
+
});
|
|
474
|
+
}
|
|
475
|
+
function buildPublicInputValue(publicInput, value) {
|
|
476
|
+
return {
|
|
477
|
+
sourceNodeId: `flow_input:${publicInput.key}`,
|
|
478
|
+
sourceHandle: publicInput.key,
|
|
479
|
+
targetHandle: publicInput.key,
|
|
480
|
+
text: coerceText(value),
|
|
481
|
+
json: coerceJsonObject(value)
|
|
482
|
+
};
|
|
483
|
+
}
|
|
484
|
+
function buildOutputMap(primaryText, primaryJson, outputs = []) {
|
|
485
|
+
const outputMap = {};
|
|
486
|
+
const declaredOutputs = outputs.length > 0 ? outputs : [{ key: "summary" }];
|
|
487
|
+
declaredOutputs.forEach((output, index) => {
|
|
488
|
+
const value = primaryJson && output.key in primaryJson
|
|
489
|
+
? primaryJson[output.key]
|
|
490
|
+
: index === 0 || output.key === "summary"
|
|
491
|
+
? primaryText
|
|
492
|
+
: null;
|
|
493
|
+
outputMap[output.key] = {
|
|
494
|
+
text: coerceText(value),
|
|
495
|
+
json: value && typeof value === "object" && !Array.isArray(value)
|
|
496
|
+
? value
|
|
497
|
+
: null
|
|
498
|
+
};
|
|
499
|
+
});
|
|
500
|
+
return outputMap;
|
|
501
|
+
}
|
|
502
|
+
function readOutputSelection(value, handle) {
|
|
503
|
+
if (!handle) {
|
|
504
|
+
const lead = Object.values(value.outputMap)[0];
|
|
505
|
+
return lead ?? { text: value.text, json: value.json };
|
|
506
|
+
}
|
|
507
|
+
const selected = value.outputMap[handle];
|
|
508
|
+
if (selected) {
|
|
509
|
+
return selected;
|
|
510
|
+
}
|
|
511
|
+
if (value.json && handle in value.json) {
|
|
512
|
+
const raw = value.json[handle];
|
|
513
|
+
return {
|
|
514
|
+
text: coerceText(raw),
|
|
515
|
+
json: raw && typeof raw === "object" && !Array.isArray(raw)
|
|
516
|
+
? raw
|
|
517
|
+
: null
|
|
518
|
+
};
|
|
519
|
+
}
|
|
520
|
+
return { text: value.text, json: value.json };
|
|
521
|
+
}
|
|
522
|
+
function defaultOutputsForNode(node) {
|
|
523
|
+
if (node.data.outputs?.length) {
|
|
524
|
+
return node.data.outputs.map((port) => normalizeWorkbenchPortDefinition(port));
|
|
525
|
+
}
|
|
526
|
+
switch (node.type) {
|
|
527
|
+
case "user_input":
|
|
528
|
+
return [{ key: "message", label: "Message", kind: "text" }];
|
|
529
|
+
case "value":
|
|
530
|
+
return [{ key: "value", label: "Value", kind: "record" }];
|
|
531
|
+
case "merge":
|
|
532
|
+
return [{ key: "merged", label: "Merged context", kind: "context" }];
|
|
533
|
+
case "template":
|
|
534
|
+
return [{ key: "rendered", label: "Rendered output", kind: "markdown" }];
|
|
535
|
+
case "pick_key":
|
|
536
|
+
return [{ key: "selected", label: "Selected value", kind: "record" }];
|
|
537
|
+
case "functor":
|
|
538
|
+
case "chat":
|
|
539
|
+
return [{ key: "answer", label: "Answer", kind: "markdown" }];
|
|
540
|
+
case "output":
|
|
541
|
+
return [{ key: node.data.outputKey?.trim() || "result", label: "Published result", kind: "record" }];
|
|
542
|
+
case "box":
|
|
543
|
+
case "box_input":
|
|
544
|
+
return [{ key: "summary", label: "Summary", kind: "summary" }];
|
|
545
|
+
}
|
|
546
|
+
}
|
|
547
|
+
function defaultInputsForNode(node) {
|
|
548
|
+
if (node.data.inputs?.length) {
|
|
549
|
+
return node.data.inputs.map((port) => normalizeWorkbenchPortDefinition(port));
|
|
550
|
+
}
|
|
551
|
+
switch (node.type) {
|
|
552
|
+
case "functor":
|
|
553
|
+
case "chat":
|
|
554
|
+
return [{ key: "input", label: "Flow input", kind: "context" }];
|
|
555
|
+
case "merge":
|
|
556
|
+
return [
|
|
557
|
+
{ key: "left", label: "Left input", kind: "context" },
|
|
558
|
+
{ key: "right", label: "Right input", kind: "context" }
|
|
559
|
+
];
|
|
560
|
+
case "template":
|
|
561
|
+
return [{ key: "input", label: "Template input", kind: "context" }];
|
|
562
|
+
case "pick_key":
|
|
563
|
+
return [{ key: "object", label: "Source object", kind: "object" }];
|
|
564
|
+
case "output":
|
|
565
|
+
return [{ key: "result", label: "Published result", kind: "record" }];
|
|
566
|
+
default:
|
|
567
|
+
return [];
|
|
568
|
+
}
|
|
569
|
+
}
|
|
570
|
+
function normalizeConnectorNodeContracts(node) {
|
|
571
|
+
const normalizePorts = (ports, direction) => ports.map((port) => {
|
|
572
|
+
const normalized = normalizeWorkbenchPortDefinition(port);
|
|
573
|
+
if (normalized.key !== "primary") {
|
|
574
|
+
return normalized;
|
|
575
|
+
}
|
|
576
|
+
const nextKey = direction === "output"
|
|
577
|
+
? node.type === "functor" || node.type === "chat"
|
|
578
|
+
? "answer"
|
|
579
|
+
: node.type === "box" || node.type === "box_input"
|
|
580
|
+
? "summary"
|
|
581
|
+
: node.type === "value"
|
|
582
|
+
? "value"
|
|
583
|
+
: node.type === "merge"
|
|
584
|
+
? "merged"
|
|
585
|
+
: node.type === "template"
|
|
586
|
+
? "rendered"
|
|
587
|
+
: node.type === "pick_key"
|
|
588
|
+
? "selected"
|
|
589
|
+
: "result"
|
|
590
|
+
: node.type === "functor" || node.type === "chat"
|
|
591
|
+
? "input"
|
|
592
|
+
: node.type === "output"
|
|
593
|
+
? "result"
|
|
594
|
+
: normalized.key;
|
|
595
|
+
return normalizeWorkbenchPortDefinition({
|
|
596
|
+
...normalized,
|
|
597
|
+
key: nextKey,
|
|
598
|
+
kind: nextKey === normalized.key ? normalized.kind : undefined
|
|
599
|
+
});
|
|
600
|
+
});
|
|
601
|
+
const normalizedInputs = normalizePorts(defaultInputsForNode(node).length > 0 ? defaultInputsForNode(node) : node.data.inputs ?? [], "input");
|
|
602
|
+
const normalizedOutputs = defaultOutputsForNode({
|
|
603
|
+
...node,
|
|
604
|
+
data: {
|
|
605
|
+
...node.data,
|
|
606
|
+
outputs: normalizePorts(node.data.outputs ?? [], "output")
|
|
607
|
+
}
|
|
608
|
+
});
|
|
609
|
+
const normalizedOutputKey = (() => {
|
|
610
|
+
const current = node.data.outputKey?.trim();
|
|
611
|
+
if (!current || current === "primary") {
|
|
612
|
+
return normalizedOutputs[0]?.key ?? "";
|
|
613
|
+
}
|
|
614
|
+
if (normalizedOutputs.some((output) => output.key === current)) {
|
|
615
|
+
return current;
|
|
616
|
+
}
|
|
617
|
+
return normalizedOutputs[0]?.key ?? current;
|
|
618
|
+
})();
|
|
619
|
+
return {
|
|
620
|
+
...node,
|
|
621
|
+
data: {
|
|
622
|
+
...node.data,
|
|
623
|
+
inputs: normalizedInputs,
|
|
624
|
+
outputs: normalizedOutputs,
|
|
625
|
+
outputKey: normalizedOutputKey
|
|
626
|
+
}
|
|
627
|
+
};
|
|
628
|
+
}
|
|
629
|
+
function canonicalEdgeHandle(handle, ports, preferred) {
|
|
630
|
+
if (ports.length === 0) {
|
|
322
631
|
return null;
|
|
323
632
|
}
|
|
633
|
+
if (!handle || handle === "primary") {
|
|
634
|
+
if (preferred && ports.some((port) => port.key === preferred)) {
|
|
635
|
+
return preferred;
|
|
636
|
+
}
|
|
637
|
+
return ports[0]?.key ?? null;
|
|
638
|
+
}
|
|
639
|
+
if (ports.some((port) => port.key === handle)) {
|
|
640
|
+
return handle;
|
|
641
|
+
}
|
|
642
|
+
if (preferred && ports.some((port) => port.key === preferred)) {
|
|
643
|
+
return preferred;
|
|
644
|
+
}
|
|
645
|
+
return ports[0]?.key ?? null;
|
|
646
|
+
}
|
|
647
|
+
function normalizeConnectorGraph(graph) {
|
|
648
|
+
const normalizedNodes = graph.nodes.map((node) => normalizeConnectorNodeContracts(node));
|
|
649
|
+
const nodeMap = new Map(normalizedNodes.map((node) => [node.id, node]));
|
|
650
|
+
const normalizedEdges = graph.edges.map((edge) => {
|
|
651
|
+
const sourceNode = nodeMap.get(edge.source);
|
|
652
|
+
const targetNode = nodeMap.get(edge.target);
|
|
653
|
+
return {
|
|
654
|
+
...edge,
|
|
655
|
+
sourceHandle: canonicalEdgeHandle(edge.sourceHandle, sourceNode?.data.outputs ?? [], sourceNode?.data.outputs?.[0]?.key),
|
|
656
|
+
targetHandle: canonicalEdgeHandle(edge.targetHandle, targetNode?.data.inputs ?? [], targetNode?.data.inputs?.[0]?.key)
|
|
657
|
+
};
|
|
658
|
+
});
|
|
659
|
+
return {
|
|
660
|
+
nodes: normalizedNodes,
|
|
661
|
+
edges: normalizedEdges
|
|
662
|
+
};
|
|
324
663
|
}
|
|
325
664
|
async function executeMachineTool(tool, args) {
|
|
326
665
|
if (tool === "machine_read_file") {
|
|
@@ -360,9 +699,15 @@ function getConversationBasePrompt(input) {
|
|
|
360
699
|
return [
|
|
361
700
|
input.node.data.prompt?.trim() || "",
|
|
362
701
|
input.userInput ? `User input:\n${input.userInput}` : "",
|
|
702
|
+
input.conversation && input.node.type === "chat" && input.conversation.transcript.length > 0
|
|
703
|
+
? `Conversation history:\n${input.conversation.transcript
|
|
704
|
+
.slice(-8)
|
|
705
|
+
.map((entry) => `${entry.role}: ${entry.text}`)
|
|
706
|
+
.join("\n")}`
|
|
707
|
+
: "",
|
|
363
708
|
input.upstream.length > 0
|
|
364
709
|
? `Linked inputs:\n${input.upstream
|
|
365
|
-
.map((entry, index) => `Input ${index + 1}:\n${entry.text}${entry.json ? `\nJSON: ${JSON.stringify(entry.json)}` : ""}`)
|
|
710
|
+
.map((entry, index) => `Input ${entry.targetHandle || index + 1}:\n${entry.text}${entry.json ? `\nJSON: ${JSON.stringify(entry.json)}` : ""}`)
|
|
366
711
|
.join("\n\n")}`
|
|
367
712
|
: "",
|
|
368
713
|
input.transcript.length > 0 ? `Tool transcript:\n${input.transcript.join("\n\n")}` : ""
|
|
@@ -445,7 +790,7 @@ function resolveConnectorModelProfile(node, secrets) {
|
|
|
445
790
|
: credential?.kind === "oauth"
|
|
446
791
|
? credential.access
|
|
447
792
|
: null;
|
|
448
|
-
if (!explicitApiKey) {
|
|
793
|
+
if (!explicitApiKey && fallbackConnection.provider !== "mock") {
|
|
449
794
|
throw new Error("The selected connector model connection is missing a credential.");
|
|
450
795
|
}
|
|
451
796
|
const profile = {
|
|
@@ -463,7 +808,7 @@ function resolveConnectorModelProfile(node, secrets) {
|
|
|
463
808
|
};
|
|
464
809
|
return {
|
|
465
810
|
profile,
|
|
466
|
-
apiKey: explicitApiKey
|
|
811
|
+
apiKey: explicitApiKey ?? "mock"
|
|
467
812
|
};
|
|
468
813
|
}
|
|
469
814
|
async function runModelNode(input) {
|
|
@@ -486,7 +831,7 @@ async function runModelNode(input) {
|
|
|
486
831
|
'For a final answer return {"action":"final","text":"..."}',
|
|
487
832
|
'For a tool call return {"action":"tool","tool":"tool_key","args":{...}}',
|
|
488
833
|
`Available tools: ${activeTools
|
|
489
|
-
.map((tool) => `${tool.key} (${tool.description})`)
|
|
834
|
+
.map((tool) => `${tool.key} (${tool.description})${tool.argsSchema ? ` args=${JSON.stringify(tool.argsSchema)}` : ""}`)
|
|
490
835
|
.join("; ")}.`
|
|
491
836
|
].join(" ")
|
|
492
837
|
: "Return only the final answer text."
|
|
@@ -498,7 +843,8 @@ async function runModelNode(input) {
|
|
|
498
843
|
node: input.node,
|
|
499
844
|
userInput: input.userInput,
|
|
500
845
|
upstream: input.upstream,
|
|
501
|
-
transcript
|
|
846
|
+
transcript,
|
|
847
|
+
conversation: input.conversation
|
|
502
848
|
});
|
|
503
849
|
let rawText = "";
|
|
504
850
|
if (conversationAware && isOpenAiFamily(profile)) {
|
|
@@ -522,27 +868,44 @@ async function runModelNode(input) {
|
|
|
522
868
|
if (activeTools.length === 0) {
|
|
523
869
|
return {
|
|
524
870
|
text: rawText.trim(),
|
|
525
|
-
|
|
871
|
+
json: tryParseJsonObject(rawText.trim()),
|
|
872
|
+
conversationId,
|
|
873
|
+
logs: transcript,
|
|
874
|
+
availableTools: []
|
|
526
875
|
};
|
|
527
876
|
}
|
|
528
877
|
const structured = tryParseStructuredAgentResponse(rawText.trim());
|
|
529
878
|
if (!structured || structured.action === "final") {
|
|
530
879
|
return {
|
|
531
880
|
text: structured?.text?.trim() || rawText.trim(),
|
|
532
|
-
|
|
881
|
+
json: tryParseJsonObject(structured?.text?.trim() || rawText.trim()),
|
|
882
|
+
conversationId,
|
|
883
|
+
logs: transcript,
|
|
884
|
+
availableTools: activeTools.map((tool) => tool.key)
|
|
533
885
|
};
|
|
534
886
|
}
|
|
535
887
|
const toolResult = structured.tool.startsWith("machine_")
|
|
536
888
|
? await executeMachineTool(structured.tool, structured.args)
|
|
537
|
-
: await executeForgeBoxTool(activeTools.find((tool) => tool.key === structured.tool)?.boxId ?? "", structured.tool, structured.args
|
|
889
|
+
: await executeForgeBoxTool(activeTools.find((tool) => tool.key === structured.tool)?.boxId ?? "", structured.tool, structured.args, {
|
|
890
|
+
actor: {
|
|
891
|
+
userIds: null,
|
|
892
|
+
source: "agent"
|
|
893
|
+
}
|
|
894
|
+
});
|
|
538
895
|
transcript.push(`Tool call ${structured.tool}: ${JSON.stringify(structured.args)}`, `Tool result: ${JSON.stringify(toolResult)}`);
|
|
539
896
|
}
|
|
540
897
|
return {
|
|
541
898
|
text: "Connector stopped after reaching the maximum tool step count.",
|
|
542
|
-
|
|
899
|
+
json: null,
|
|
900
|
+
conversationId,
|
|
901
|
+
logs: transcript,
|
|
902
|
+
availableTools: activeTools.map((tool) => tool.key)
|
|
543
903
|
};
|
|
544
904
|
}
|
|
545
905
|
function validateConnectorGraph(graph) {
|
|
906
|
+
if (graph.nodes.length === 0) {
|
|
907
|
+
throw new Error("Connector graph has no nodes yet.");
|
|
908
|
+
}
|
|
546
909
|
const nodeIds = new Set(graph.nodes.map((node) => node.id));
|
|
547
910
|
for (const edge of graph.edges) {
|
|
548
911
|
if (!nodeIds.has(edge.source) || !nodeIds.has(edge.target)) {
|
|
@@ -574,8 +937,44 @@ function validateConnectorGraph(graph) {
|
|
|
574
937
|
for (const node of graph.nodes) {
|
|
575
938
|
visit(node.id);
|
|
576
939
|
}
|
|
940
|
+
const incomingCounts = new Map();
|
|
941
|
+
const outgoingCounts = new Map();
|
|
942
|
+
for (const edge of graph.edges) {
|
|
943
|
+
incomingCounts.set(edge.target, (incomingCounts.get(edge.target) ?? 0) + 1);
|
|
944
|
+
outgoingCounts.set(edge.source, (outgoingCounts.get(edge.source) ?? 0) + 1);
|
|
945
|
+
}
|
|
946
|
+
const outputNodes = graph.nodes.filter((node) => node.type === "output");
|
|
947
|
+
if (outputNodes.length === 0) {
|
|
948
|
+
throw new Error("Connector graph is missing an output node.");
|
|
949
|
+
}
|
|
950
|
+
const disconnectedOutput = outputNodes.find((node) => (incomingCounts.get(node.id) ?? 0) === 0);
|
|
951
|
+
if (disconnectedOutput) {
|
|
952
|
+
throw new Error(`Output node "${disconnectedOutput.data.label || disconnectedOutput.id}" has no incoming connection.`);
|
|
953
|
+
}
|
|
954
|
+
const aiNodeMissingPrompt = graph.nodes.find((node) => (node.type === "functor" || node.type === "chat") &&
|
|
955
|
+
!(node.data.promptTemplate?.trim() || node.data.prompt?.trim()));
|
|
956
|
+
if (aiNodeMissingPrompt) {
|
|
957
|
+
throw new Error(`AI node "${aiNodeMissingPrompt.data.label || aiNodeMissingPrompt.id}" is missing a prompt.`);
|
|
958
|
+
}
|
|
959
|
+
const mergeNodeMissingInputs = graph.nodes.find((node) => node.type === "merge" && (incomingCounts.get(node.id) ?? 0) < 2);
|
|
960
|
+
if (mergeNodeMissingInputs) {
|
|
961
|
+
throw new Error(`Merge node "${mergeNodeMissingInputs.data.label || mergeNodeMissingInputs.id}" must receive both left and right inputs.`);
|
|
962
|
+
}
|
|
963
|
+
const templateNodeMissingTemplate = graph.nodes.find((node) => node.type === "template" && !(node.data.template ?? "").trim());
|
|
964
|
+
if (templateNodeMissingTemplate) {
|
|
965
|
+
throw new Error(`Template node "${templateNodeMissingTemplate.data.label || templateNodeMissingTemplate.id}" is missing its template string.`);
|
|
966
|
+
}
|
|
967
|
+
const pickKeyNodeMissingSelection = graph.nodes.find((node) => node.type === "pick_key" && !(node.data.selectedKey ?? "").trim());
|
|
968
|
+
if (pickKeyNodeMissingSelection) {
|
|
969
|
+
throw new Error(`Pick-key node "${pickKeyNodeMissingSelection.data.label || pickKeyNodeMissingSelection.id}" is missing the key it should select.`);
|
|
970
|
+
}
|
|
971
|
+
const isolatedNode = graph.nodes.find((node) => node.type !== "output" &&
|
|
972
|
+
(outgoingCounts.get(node.id) ?? 0) === 0);
|
|
973
|
+
if (isolatedNode) {
|
|
974
|
+
throw new Error(`Node "${isolatedNode.data.label || isolatedNode.id}" is not connected to anything downstream.`);
|
|
975
|
+
}
|
|
577
976
|
}
|
|
578
|
-
function buildOutputResult(connector, resolvedNodeValues) {
|
|
977
|
+
function buildOutputResult(connector, resolvedNodeValues, nodeResults) {
|
|
579
978
|
const outputs = Object.fromEntries(connector.publishedOutputs.map((output) => {
|
|
580
979
|
const nodeValue = resolvedNodeValues.get(output.nodeId);
|
|
581
980
|
return [
|
|
@@ -590,9 +989,31 @@ function buildOutputResult(connector, resolvedNodeValues) {
|
|
|
590
989
|
const first = connector.publishedOutputs[0];
|
|
591
990
|
return aiConnectorRunResultSchema.parse({
|
|
592
991
|
primaryText: first ? outputs[first.id]?.text ?? "" : "",
|
|
593
|
-
outputs
|
|
992
|
+
outputs,
|
|
993
|
+
nodeResults
|
|
594
994
|
});
|
|
595
995
|
}
|
|
996
|
+
function parseValueLiteral(valueType, valueLiteral) {
|
|
997
|
+
if (valueType === "null") {
|
|
998
|
+
return null;
|
|
999
|
+
}
|
|
1000
|
+
if (valueType === "boolean") {
|
|
1001
|
+
return valueLiteral.trim().toLowerCase() === "true";
|
|
1002
|
+
}
|
|
1003
|
+
if (valueType === "number") {
|
|
1004
|
+
const parsed = Number(valueLiteral);
|
|
1005
|
+
return Number.isFinite(parsed) ? parsed : 0;
|
|
1006
|
+
}
|
|
1007
|
+
if (valueType === "array" || valueType === "object") {
|
|
1008
|
+
try {
|
|
1009
|
+
return JSON.parse(valueLiteral || (valueType === "array" ? "[]" : "{}"));
|
|
1010
|
+
}
|
|
1011
|
+
catch {
|
|
1012
|
+
return valueType === "array" ? [] : {};
|
|
1013
|
+
}
|
|
1014
|
+
}
|
|
1015
|
+
return valueLiteral;
|
|
1016
|
+
}
|
|
596
1017
|
function createConversationRecord(input) {
|
|
597
1018
|
const now = new Date().toISOString();
|
|
598
1019
|
return saveAiConnectorConversation(aiConnectorConversationSchema.parse({
|
|
@@ -615,10 +1036,51 @@ async function executeConnector(connector, rawInput, services) {
|
|
|
615
1036
|
incoming.set(edge.target, list);
|
|
616
1037
|
}
|
|
617
1038
|
const values = new Map();
|
|
1039
|
+
const debugNodes = [];
|
|
1040
|
+
const nodeResults = [];
|
|
1041
|
+
const debugErrors = [];
|
|
618
1042
|
const outputNodes = connector.graph.nodes.filter((node) => node.type === "output");
|
|
619
1043
|
const activeConversation = parsedInput.conversationId
|
|
620
1044
|
? getAiConnectorConversationById(parsedInput.conversationId)
|
|
621
1045
|
: getAiConnectorConversationForConnector(connector.id);
|
|
1046
|
+
const publicInputValues = new Map();
|
|
1047
|
+
const nodePublicInputs = new Map();
|
|
1048
|
+
const nodePublicParams = new Map();
|
|
1049
|
+
for (const publicInput of connector.publicInputs) {
|
|
1050
|
+
const hasProvided = Object.prototype.hasOwnProperty.call(parsedInput.inputs, publicInput.key);
|
|
1051
|
+
const resolvedValue = hasProvided
|
|
1052
|
+
? parsedInput.inputs[publicInput.key]
|
|
1053
|
+
: publicInput.defaultValue;
|
|
1054
|
+
if (resolvedValue === undefined) {
|
|
1055
|
+
if (publicInput.required) {
|
|
1056
|
+
throw new Error(`Flow input "${publicInput.label}" is required.`);
|
|
1057
|
+
}
|
|
1058
|
+
continue;
|
|
1059
|
+
}
|
|
1060
|
+
if (!validatePortValueType(publicInput, resolvedValue)) {
|
|
1061
|
+
throw new Error(`Flow input "${publicInput.label}" must match the ${publicInput.kind} type.`);
|
|
1062
|
+
}
|
|
1063
|
+
const bindings = normalizePublicInputBindings(connector, publicInput);
|
|
1064
|
+
if (bindings.length === 0) {
|
|
1065
|
+
throw new Error(`Flow input "${publicInput.label}" is not bound to any node input or parameter yet.`);
|
|
1066
|
+
}
|
|
1067
|
+
publicInputValues.set(publicInput.key, resolvedValue);
|
|
1068
|
+
for (const binding of bindings) {
|
|
1069
|
+
if (binding.targetKind === "param") {
|
|
1070
|
+
const current = nodePublicParams.get(binding.nodeId) ?? {};
|
|
1071
|
+
current[binding.targetKey] = resolvedValue;
|
|
1072
|
+
nodePublicParams.set(binding.nodeId, current);
|
|
1073
|
+
continue;
|
|
1074
|
+
}
|
|
1075
|
+
const current = nodePublicInputs.get(binding.nodeId) ?? [];
|
|
1076
|
+
const publicBindingValue = buildPublicInputValue(publicInput, resolvedValue);
|
|
1077
|
+
current.push({
|
|
1078
|
+
...publicBindingValue,
|
|
1079
|
+
targetHandle: binding.targetKey
|
|
1080
|
+
});
|
|
1081
|
+
nodePublicInputs.set(binding.nodeId, current);
|
|
1082
|
+
}
|
|
1083
|
+
}
|
|
622
1084
|
const evaluateNode = async (nodeId) => {
|
|
623
1085
|
const existing = values.get(nodeId);
|
|
624
1086
|
if (existing) {
|
|
@@ -628,18 +1090,90 @@ async function executeConnector(connector, rawInput, services) {
|
|
|
628
1090
|
if (!node) {
|
|
629
1091
|
throw new Error(`Missing connector node ${nodeId}.`);
|
|
630
1092
|
}
|
|
631
|
-
const
|
|
1093
|
+
const startedAt = Date.now();
|
|
1094
|
+
const upstreamEdges = incoming.get(nodeId) ?? [];
|
|
1095
|
+
const graphUpstream = await Promise.all(upstreamEdges.map(async (edge) => {
|
|
1096
|
+
const upstreamValue = await evaluateNode(edge.source);
|
|
1097
|
+
const selected = readOutputSelection(upstreamValue, edge.sourceHandle);
|
|
1098
|
+
return {
|
|
1099
|
+
edge,
|
|
1100
|
+
sourceValue: upstreamValue,
|
|
1101
|
+
selected
|
|
1102
|
+
};
|
|
1103
|
+
}));
|
|
1104
|
+
const publicInputs = (nodePublicInputs.get(nodeId) ?? []).map((entry) => ({
|
|
1105
|
+
edge: {
|
|
1106
|
+
id: `${nodeId}_${entry.targetHandle}`,
|
|
1107
|
+
source: entry.sourceNodeId,
|
|
1108
|
+
target: nodeId,
|
|
1109
|
+
sourceHandle: entry.sourceHandle,
|
|
1110
|
+
targetHandle: entry.targetHandle,
|
|
1111
|
+
label: null
|
|
1112
|
+
},
|
|
1113
|
+
sourceValue: {
|
|
1114
|
+
text: entry.text,
|
|
1115
|
+
json: entry.json,
|
|
1116
|
+
tools: [],
|
|
1117
|
+
conversationId: null,
|
|
1118
|
+
outputMap: {
|
|
1119
|
+
[entry.sourceHandle ?? entry.targetHandle]: {
|
|
1120
|
+
text: entry.text,
|
|
1121
|
+
json: entry.json
|
|
1122
|
+
}
|
|
1123
|
+
},
|
|
1124
|
+
logs: []
|
|
1125
|
+
},
|
|
1126
|
+
selected: {
|
|
1127
|
+
text: entry.text,
|
|
1128
|
+
json: entry.json
|
|
1129
|
+
}
|
|
1130
|
+
}));
|
|
1131
|
+
const upstream = [...graphUpstream, ...publicInputs];
|
|
1132
|
+
const resolvedInputsForDebug = upstream.map((entry) => ({
|
|
1133
|
+
sourceNodeId: entry.edge.source,
|
|
1134
|
+
sourceHandle: entry.edge.sourceHandle ?? null,
|
|
1135
|
+
targetHandle: entry.edge.targetHandle ?? null,
|
|
1136
|
+
text: entry.selected.text,
|
|
1137
|
+
json: entry.selected.json
|
|
1138
|
+
}));
|
|
632
1139
|
let resolved;
|
|
633
|
-
|
|
1140
|
+
let nodeToolKeys = [];
|
|
1141
|
+
if (node.type === "box" || node.type === "box_input") {
|
|
634
1142
|
const boxId = node.data.boxId?.trim() || "";
|
|
1143
|
+
const resolvedInputs = Object.fromEntries(upstream.map(({ edge, selected }, index) => [
|
|
1144
|
+
edge.targetHandle ?? edge.sourceHandle ?? `input_${index + 1}`,
|
|
1145
|
+
selected.json ?? selected.text
|
|
1146
|
+
]));
|
|
1147
|
+
const resolvedParams = {
|
|
1148
|
+
...(node.data.paramValues && typeof node.data.paramValues === "object"
|
|
1149
|
+
? node.data.paramValues
|
|
1150
|
+
: {}),
|
|
1151
|
+
...(nodePublicParams.get(nodeId) ?? {})
|
|
1152
|
+
};
|
|
635
1153
|
const providedSnapshot = boxId ? parsedInput.boxSnapshots[boxId] : null;
|
|
636
1154
|
const snapshot = providedSnapshot && typeof providedSnapshot === "object"
|
|
637
1155
|
? {
|
|
638
|
-
...resolveForgeBoxSnapshot(boxId
|
|
1156
|
+
...resolveForgeBoxSnapshot(boxId, {
|
|
1157
|
+
actor: {
|
|
1158
|
+
userIds: null,
|
|
1159
|
+
source: "agent"
|
|
1160
|
+
}
|
|
1161
|
+
}, {
|
|
1162
|
+
inputs: resolvedInputs,
|
|
1163
|
+
params: resolvedParams
|
|
1164
|
+
}),
|
|
639
1165
|
contentJson: providedSnapshot
|
|
640
1166
|
}
|
|
641
1167
|
: boxId
|
|
642
|
-
? resolveForgeBoxSnapshot(boxId
|
|
1168
|
+
? resolveForgeBoxSnapshot(boxId, {
|
|
1169
|
+
actor: {
|
|
1170
|
+
userIds: null,
|
|
1171
|
+
source: "agent"
|
|
1172
|
+
}
|
|
1173
|
+
}, {
|
|
1174
|
+
inputs: resolvedInputs,
|
|
1175
|
+
params: resolvedParams
|
|
1176
|
+
})
|
|
643
1177
|
: {
|
|
644
1178
|
boxId: "",
|
|
645
1179
|
label: node.data.label,
|
|
@@ -648,6 +1182,7 @@ async function executeConnector(connector, rawInput, services) {
|
|
|
648
1182
|
contentJson: null,
|
|
649
1183
|
tools: []
|
|
650
1184
|
};
|
|
1185
|
+
const outputDefs = defaultOutputsForNode(node);
|
|
651
1186
|
resolved = {
|
|
652
1187
|
text: snapshot.contentText,
|
|
653
1188
|
json: snapshot.contentJson,
|
|
@@ -655,26 +1190,148 @@ async function executeConnector(connector, rawInput, services) {
|
|
|
655
1190
|
boxId: snapshot.boxId,
|
|
656
1191
|
key: tool.key,
|
|
657
1192
|
label: tool.label,
|
|
658
|
-
description: tool.description
|
|
1193
|
+
description: tool.description,
|
|
1194
|
+
argsSchema: tool.argsSchema
|
|
659
1195
|
})),
|
|
660
|
-
conversationId: null
|
|
1196
|
+
conversationId: null,
|
|
1197
|
+
outputMap: buildOutputMap(snapshot.contentText, snapshot.contentJson, outputDefs),
|
|
1198
|
+
logs: []
|
|
1199
|
+
};
|
|
1200
|
+
nodeToolKeys = resolved.tools.map((tool) => tool.key);
|
|
1201
|
+
}
|
|
1202
|
+
else if (node.type === "value") {
|
|
1203
|
+
const parsedValue = parseValueLiteral(node.data.valueType ?? "string", node.data.valueLiteral ?? "");
|
|
1204
|
+
const jsonValue = parsedValue && typeof parsedValue === "object" && !Array.isArray(parsedValue)
|
|
1205
|
+
? parsedValue
|
|
1206
|
+
: null;
|
|
1207
|
+
const textValue = parsedValue === null
|
|
1208
|
+
? "null"
|
|
1209
|
+
: typeof parsedValue === "string"
|
|
1210
|
+
? parsedValue
|
|
1211
|
+
: JSON.stringify(parsedValue, null, 2);
|
|
1212
|
+
resolved = {
|
|
1213
|
+
text: textValue,
|
|
1214
|
+
json: jsonValue,
|
|
1215
|
+
tools: [],
|
|
1216
|
+
conversationId: null,
|
|
1217
|
+
outputMap: buildOutputMap(textValue, jsonValue, defaultOutputsForNode(node)),
|
|
1218
|
+
logs: []
|
|
661
1219
|
};
|
|
662
1220
|
}
|
|
663
1221
|
else if (node.type === "user_input") {
|
|
1222
|
+
const inputJson = Object.keys(parsedInput.context).length > 0
|
|
1223
|
+
? {
|
|
1224
|
+
message: parsedInput.userInput || "",
|
|
1225
|
+
inputs: Object.fromEntries(publicInputValues),
|
|
1226
|
+
context: parsedInput.context
|
|
1227
|
+
}
|
|
1228
|
+
: {
|
|
1229
|
+
message: parsedInput.userInput || "",
|
|
1230
|
+
inputs: Object.fromEntries(publicInputValues)
|
|
1231
|
+
};
|
|
664
1232
|
resolved = {
|
|
665
1233
|
text: parsedInput.userInput || "",
|
|
666
|
-
json:
|
|
1234
|
+
json: inputJson,
|
|
667
1235
|
tools: [],
|
|
668
|
-
conversationId: activeConversation?.id ?? null
|
|
1236
|
+
conversationId: activeConversation?.id ?? null,
|
|
1237
|
+
outputMap: buildOutputMap(parsedInput.userInput || "", inputJson, defaultOutputsForNode(node)),
|
|
1238
|
+
logs: []
|
|
1239
|
+
};
|
|
1240
|
+
}
|
|
1241
|
+
else if (node.type === "merge") {
|
|
1242
|
+
const mergedText = upstream
|
|
1243
|
+
.map((entry) => entry.selected.text)
|
|
1244
|
+
.filter(Boolean)
|
|
1245
|
+
.join("\n\n");
|
|
1246
|
+
const mergedJson = Object.assign({}, ...upstream
|
|
1247
|
+
.map((entry) => entry.selected.json)
|
|
1248
|
+
.filter((entry) => Boolean(entry) && typeof entry === "object"));
|
|
1249
|
+
resolved = {
|
|
1250
|
+
text: mergedText,
|
|
1251
|
+
json: Object.keys(mergedJson).length > 0
|
|
1252
|
+
? {
|
|
1253
|
+
merged: mergedJson
|
|
1254
|
+
}
|
|
1255
|
+
: {
|
|
1256
|
+
merged: mergedText
|
|
1257
|
+
},
|
|
1258
|
+
tools: upstream.flatMap((entry) => entry.sourceValue.tools),
|
|
1259
|
+
conversationId: upstream.find((entry) => entry.sourceValue.conversationId)?.sourceValue
|
|
1260
|
+
.conversationId ?? null,
|
|
1261
|
+
outputMap: buildOutputMap(mergedText, Object.keys(mergedJson).length > 0
|
|
1262
|
+
? {
|
|
1263
|
+
merged: mergedJson
|
|
1264
|
+
}
|
|
1265
|
+
: {
|
|
1266
|
+
merged: mergedText
|
|
1267
|
+
}, defaultOutputsForNode(node)),
|
|
1268
|
+
logs: []
|
|
1269
|
+
};
|
|
1270
|
+
}
|
|
1271
|
+
else if (node.type === "template") {
|
|
1272
|
+
const primary = upstream[0]?.selected ?? { text: "", json: null };
|
|
1273
|
+
const rendered = (node.data.template ?? node.data.promptTemplate ?? "")
|
|
1274
|
+
.replaceAll("{{input}}", primary.text)
|
|
1275
|
+
.replaceAll("{{json}}", primary.json ? JSON.stringify(primary.json) : "");
|
|
1276
|
+
resolved = {
|
|
1277
|
+
text: rendered,
|
|
1278
|
+
json: {
|
|
1279
|
+
rendered,
|
|
1280
|
+
...(tryParseJsonObject(rendered) ?? {})
|
|
1281
|
+
},
|
|
1282
|
+
tools: [],
|
|
1283
|
+
conversationId: upstream.find((entry) => entry.sourceValue.conversationId)?.sourceValue
|
|
1284
|
+
.conversationId ?? null,
|
|
1285
|
+
outputMap: buildOutputMap(rendered, {
|
|
1286
|
+
rendered,
|
|
1287
|
+
...(tryParseJsonObject(rendered) ?? {})
|
|
1288
|
+
}, defaultOutputsForNode(node)),
|
|
1289
|
+
logs: []
|
|
1290
|
+
};
|
|
1291
|
+
}
|
|
1292
|
+
else if (node.type === "pick_key") {
|
|
1293
|
+
const primary = upstream[0]?.selected ?? { text: "", json: null };
|
|
1294
|
+
const selectedKey = node.data.selectedKey?.trim() || "";
|
|
1295
|
+
const selectedValue = primary.json && selectedKey in primary.json ? primary.json[selectedKey] : null;
|
|
1296
|
+
const selectedJson = selectedValue &&
|
|
1297
|
+
typeof selectedValue === "object" &&
|
|
1298
|
+
!Array.isArray(selectedValue)
|
|
1299
|
+
? selectedValue
|
|
1300
|
+
: null;
|
|
1301
|
+
resolved = {
|
|
1302
|
+
text: coerceText(selectedValue),
|
|
1303
|
+
json: selectedJson ?? {
|
|
1304
|
+
selected: selectedValue
|
|
1305
|
+
},
|
|
1306
|
+
tools: [],
|
|
1307
|
+
conversationId: upstream.find((entry) => entry.sourceValue.conversationId)?.sourceValue
|
|
1308
|
+
.conversationId ?? null,
|
|
1309
|
+
outputMap: buildOutputMap(coerceText(selectedValue), selectedJson ?? {
|
|
1310
|
+
selected: selectedValue
|
|
1311
|
+
}, defaultOutputsForNode(node)),
|
|
1312
|
+
logs: []
|
|
669
1313
|
};
|
|
670
1314
|
}
|
|
671
1315
|
else if (node.type === "output") {
|
|
672
|
-
const
|
|
1316
|
+
const outputHandle = node.data.outputKey?.trim() || null;
|
|
1317
|
+
const publishedSelections = upstream.map((entry) => readOutputSelection(entry.sourceValue, outputHandle ?? entry.edge.sourceHandle));
|
|
1318
|
+
const mergedText = publishedSelections
|
|
1319
|
+
.map((entry) => entry.text)
|
|
1320
|
+
.filter(Boolean)
|
|
1321
|
+
.join("\n\n");
|
|
1322
|
+
const leadSelection = publishedSelections[0] ?? { text: mergedText, json: null };
|
|
1323
|
+
const publishedKey = outputHandle || "result";
|
|
1324
|
+
const publishedJson = leadSelection.json ?? {
|
|
1325
|
+
[publishedKey]: leadSelection.text
|
|
1326
|
+
};
|
|
673
1327
|
resolved = {
|
|
674
1328
|
text: mergedText,
|
|
675
|
-
json:
|
|
1329
|
+
json: publishedJson,
|
|
676
1330
|
tools: [],
|
|
677
|
-
conversationId: upstream.find((entry) => entry.conversationId)?.
|
|
1331
|
+
conversationId: upstream.find((entry) => entry.sourceValue.conversationId)?.sourceValue
|
|
1332
|
+
.conversationId ?? null,
|
|
1333
|
+
outputMap: buildOutputMap(mergedText, publishedJson, defaultOutputsForNode(node)),
|
|
1334
|
+
logs: []
|
|
678
1335
|
};
|
|
679
1336
|
}
|
|
680
1337
|
else {
|
|
@@ -682,24 +1339,76 @@ async function executeConnector(connector, rawInput, services) {
|
|
|
682
1339
|
connector,
|
|
683
1340
|
node,
|
|
684
1341
|
userInput: parsedInput.userInput,
|
|
685
|
-
upstream
|
|
1342
|
+
upstream: upstream.map((entry) => ({
|
|
1343
|
+
text: entry.selected.text,
|
|
1344
|
+
json: entry.selected.json,
|
|
1345
|
+
tools: entry.sourceValue.tools,
|
|
1346
|
+
conversationId: entry.sourceValue.conversationId,
|
|
1347
|
+
outputMap: entry.sourceValue.outputMap,
|
|
1348
|
+
logs: entry.sourceValue.logs,
|
|
1349
|
+
targetHandle: entry.edge.targetHandle ?? null
|
|
1350
|
+
})),
|
|
686
1351
|
services,
|
|
687
1352
|
conversation: activeConversation
|
|
688
1353
|
});
|
|
1354
|
+
const outputDefs = defaultOutputsForNode(node);
|
|
689
1355
|
resolved = {
|
|
690
1356
|
text: modelResult.text,
|
|
691
|
-
json:
|
|
1357
|
+
json: modelResult.json,
|
|
692
1358
|
tools: [],
|
|
693
|
-
conversationId: modelResult.conversationId
|
|
1359
|
+
conversationId: modelResult.conversationId,
|
|
1360
|
+
outputMap: buildOutputMap(modelResult.text, modelResult.json, outputDefs),
|
|
1361
|
+
logs: modelResult.logs
|
|
694
1362
|
};
|
|
1363
|
+
nodeToolKeys = modelResult.availableTools;
|
|
695
1364
|
}
|
|
696
1365
|
values.set(nodeId, resolved);
|
|
1366
|
+
debugNodes.push({
|
|
1367
|
+
nodeId: node.id,
|
|
1368
|
+
nodeType: node.type,
|
|
1369
|
+
label: node.data.label,
|
|
1370
|
+
input: resolvedInputsForDebug,
|
|
1371
|
+
output: {
|
|
1372
|
+
text: resolved.text,
|
|
1373
|
+
json: resolved.json
|
|
1374
|
+
},
|
|
1375
|
+
tools: nodeToolKeys,
|
|
1376
|
+
logs: resolved.logs,
|
|
1377
|
+
error: null
|
|
1378
|
+
});
|
|
1379
|
+
nodeResults.push({
|
|
1380
|
+
nodeId: node.id,
|
|
1381
|
+
nodeType: node.type,
|
|
1382
|
+
label: node.data.label,
|
|
1383
|
+
input: resolvedInputsForDebug,
|
|
1384
|
+
primaryText: resolved.text,
|
|
1385
|
+
payload: resolved.json,
|
|
1386
|
+
outputMap: resolved.outputMap,
|
|
1387
|
+
tools: nodeToolKeys,
|
|
1388
|
+
logs: resolved.logs,
|
|
1389
|
+
error: null,
|
|
1390
|
+
timingMs: Date.now() - startedAt
|
|
1391
|
+
});
|
|
697
1392
|
return resolved;
|
|
698
1393
|
};
|
|
699
|
-
|
|
700
|
-
|
|
1394
|
+
try {
|
|
1395
|
+
for (const outputNode of outputNodes) {
|
|
1396
|
+
await evaluateNode(outputNode.id);
|
|
1397
|
+
}
|
|
701
1398
|
}
|
|
702
|
-
|
|
1399
|
+
catch (error) {
|
|
1400
|
+
debugErrors.push(error instanceof Error ? error.message : "Flow execution failed");
|
|
1401
|
+
throw error;
|
|
1402
|
+
}
|
|
1403
|
+
const result = aiConnectorRunResultSchema.parse({
|
|
1404
|
+
...buildOutputResult(connector, values, nodeResults),
|
|
1405
|
+
debugTrace: parsedInput.debug
|
|
1406
|
+
? {
|
|
1407
|
+
nodes: debugNodes,
|
|
1408
|
+
errors: debugErrors
|
|
1409
|
+
}
|
|
1410
|
+
: undefined
|
|
1411
|
+
});
|
|
703
1412
|
const conversationProviderNode = connector.graph.nodes.find((node) => node.type === "chat");
|
|
704
1413
|
const resolvedConversationId = [...values.values()].find((entry) => entry.conversationId)?.conversationId ?? null;
|
|
705
1414
|
const nextConversation = conversationProviderNode
|
|
@@ -792,7 +1501,7 @@ function migrateLegacyProcessor(processorId) {
|
|
|
792
1501
|
data: {
|
|
793
1502
|
label: "Output",
|
|
794
1503
|
description: "Imported legacy output.",
|
|
795
|
-
outputKey: "
|
|
1504
|
+
outputKey: "answer",
|
|
796
1505
|
enabledToolKeys: []
|
|
797
1506
|
}
|
|
798
1507
|
};
|
|
@@ -852,13 +1561,13 @@ export function createAiConnector(input) {
|
|
|
852
1561
|
const now = new Date().toISOString();
|
|
853
1562
|
const id = `aic_${randomUUID().replaceAll("-", "").slice(0, 10)}`;
|
|
854
1563
|
const slug = buildConnectorSlug(parsed.title, id);
|
|
855
|
-
const graph = parsed.graph.nodes.length > 0 ? parsed.graph : buildDefaultGraph(parsed.kind, parsed.title);
|
|
1564
|
+
const graph = normalizeConnectorGraph(parsed.graph.nodes.length > 0 ? parsed.graph : buildDefaultGraph(parsed.kind, parsed.title));
|
|
856
1565
|
const publishedOutputs = ensurePublishedOutputs(id, graph);
|
|
857
1566
|
getDatabase()
|
|
858
1567
|
.prepare(`INSERT INTO ai_connectors (
|
|
859
|
-
id, slug, title, description, kind, home_surface_id, endpoint_enabled, graph_json, published_outputs_json, last_run_json, legacy_processor_id, created_at, updated_at
|
|
860
|
-
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`)
|
|
861
|
-
.run(id, slug, parsed.title, parsed.description, parsed.kind, parsed.homeSurfaceId, parsed.endpointEnabled ? 1 : 0, JSON.stringify(graph), JSON.stringify(publishedOutputs), null, input.legacyProcessorId ?? null, now, now);
|
|
1568
|
+
id, slug, title, description, kind, home_surface_id, endpoint_enabled, graph_json, public_inputs_json, published_outputs_json, last_run_json, legacy_processor_id, created_at, updated_at
|
|
1569
|
+
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`)
|
|
1570
|
+
.run(id, slug, parsed.title, parsed.description, parsed.kind, parsed.homeSurfaceId, parsed.endpointEnabled ? 1 : 0, JSON.stringify(graph), JSON.stringify(parsed.publicInputs), JSON.stringify(publishedOutputs), null, input.legacyProcessorId ?? null, now, now);
|
|
862
1571
|
return getAiConnectorById(id);
|
|
863
1572
|
}
|
|
864
1573
|
export function updateAiConnector(connectorId, patch) {
|
|
@@ -867,7 +1576,7 @@ export function updateAiConnector(connectorId, patch) {
|
|
|
867
1576
|
return null;
|
|
868
1577
|
}
|
|
869
1578
|
const parsed = updateAiConnectorSchema.parse(patch);
|
|
870
|
-
const nextGraph = parsed.graph ?? current.graph;
|
|
1579
|
+
const nextGraph = normalizeConnectorGraph(parsed.graph ?? current.graph);
|
|
871
1580
|
validateConnectorGraph(nextGraph);
|
|
872
1581
|
const nextTitle = parsed.title ?? current.title;
|
|
873
1582
|
const next = {
|
|
@@ -878,14 +1587,15 @@ export function updateAiConnector(connectorId, patch) {
|
|
|
878
1587
|
? buildConnectorSlug(parsed.title, current.id)
|
|
879
1588
|
: current.slug,
|
|
880
1589
|
graph: nextGraph,
|
|
1590
|
+
publicInputs: parsed.publicInputs ?? current.publicInputs,
|
|
881
1591
|
publishedOutputs: ensurePublishedOutputs(current.id, nextGraph)
|
|
882
1592
|
};
|
|
883
1593
|
const now = new Date().toISOString();
|
|
884
1594
|
getDatabase()
|
|
885
1595
|
.prepare(`UPDATE ai_connectors
|
|
886
|
-
SET slug = ?, title = ?, description = ?, kind = ?, home_surface_id = ?, endpoint_enabled = ?, graph_json = ?, published_outputs_json = ?, updated_at = ?
|
|
1596
|
+
SET slug = ?, title = ?, description = ?, kind = ?, home_surface_id = ?, endpoint_enabled = ?, graph_json = ?, public_inputs_json = ?, published_outputs_json = ?, updated_at = ?
|
|
887
1597
|
WHERE id = ?`)
|
|
888
|
-
.run(next.slug, next.title, next.description, next.kind, next.homeSurfaceId, next.endpointEnabled ? 1 : 0, JSON.stringify(next.graph), JSON.stringify(next.publishedOutputs), now, connectorId);
|
|
1598
|
+
.run(next.slug, next.title, next.description, next.kind, next.homeSurfaceId, next.endpointEnabled ? 1 : 0, JSON.stringify(next.graph), JSON.stringify(next.publicInputs), JSON.stringify(next.publishedOutputs), now, connectorId);
|
|
889
1599
|
return getAiConnectorById(connectorId);
|
|
890
1600
|
}
|
|
891
1601
|
export function deleteAiConnector(connectorId) {
|
|
@@ -907,6 +1617,7 @@ export async function runAiConnector(connectorId, input, services, mode = "run")
|
|
|
907
1617
|
mode,
|
|
908
1618
|
status: "running",
|
|
909
1619
|
userInput: input.userInput ?? "",
|
|
1620
|
+
inputs: input.inputs ?? {},
|
|
910
1621
|
context: input.context ?? {},
|
|
911
1622
|
conversationId: input.conversationId ?? null,
|
|
912
1623
|
result: null,
|