Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 28 additions & 0 deletions src/agent/agents/subagents/pipelineRepair.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
/**
* Pipeline-repair sub-agent — diagnoses validation issues and broken
* connections in an existing pipeline and applies fixes through the
* CSOM tool surface (which proxies to the main-thread MobX spec inside
* a single undo group).
*/
import { Agent } from "@openai/agents";

import { requireOrchestratorModel } from "../../config";
import { attachObservabilityHooks } from "../../middleware/observability";
import pipelineRepairPrompt from "../../prompts/pipelineRepair.md?raw";
import type { AgentSession } from "../../session";
import { createCsomTools } from "../../tools/csomTools";

export function createPipelineRepairAgent(session: AgentSession): Agent {
const csom = createCsomTools(session.bridge);
const agent = new Agent({
name: "pipeline-repair",
handoffDescription: `Diagnose and fix validation issues, broken connections, missing inputs, and other
structural problems in existing pipelines. Can mutate the pipeline via CSOM tools.
Asks the user for input when fixes are ambiguous.`,
instructions: pipelineRepairPrompt,
tools: csom.allTools,
model: requireOrchestratorModel(),
});
attachObservabilityHooks(agent, session.emitStatus);
return agent;
}
6 changes: 5 additions & 1 deletion src/agent/agents/tangleDispatcher.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import { attachObservabilityHooks } from "../middleware/observability";
import dispatcherPrompt from "../prompts/dispatcher.md?raw";
import type { AgentSession } from "../session";
import { createGeneralHelpAgent } from "./subagents/generalHelp";
import { createPipelineRepairAgent } from "./subagents/pipelineRepair";

interface DispatcherInvokeParams {
message: string;
Expand All @@ -37,7 +38,10 @@ function createDispatcherAgent(session: AgentSession): Agent {
model: requireOrchestratorModel(),
instructions: `${RECOMMENDED_PROMPT_PREFIX}\n\n${dispatcherPrompt}`,
tools: [],
handoffs: [createGeneralHelpAgent(session)],
handoffs: [
createGeneralHelpAgent(session),
createPipelineRepairAgent(session),
],
});
attachObservabilityHooks(agent, session.emitStatus);
return agent;
Expand Down
18 changes: 11 additions & 7 deletions src/agent/prompts/dispatcher.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,20 +4,24 @@ You are the **Tangle Dispatcher**, the entry point for the Tangle Pipeline Studi

## Available specialists

| Specialist | When to hand off |
| -------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `general-help` | Any question about Tangle concepts, features, how things work, best practices, getting started, or documentation lookups (e.g. "what is a pipeline?", "how do I connect tasks?", "what are subgraphs?"). |
| Specialist | When to hand off |
| ----------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `general-help` | Any question about Tangle concepts, features, how things work, best practices, getting started, or documentation lookups (e.g. "what is a pipeline?", "how do I connect tasks?", "what are subgraphs?"). |
| `pipeline-repair` | Any request to inspect, validate, or fix the user's current pipeline — broken connections, validation errors, dangling bindings, missing arguments, structural cleanup (e.g. "fix my pipeline", "what's wrong with this?", "clean up the validation issues", "repair the broken bindings"). |

Future releases will add specialists for building, fixing, and debugging pipelines. Until then, hand off **every** Tangle / pipeline / ML / docs question to `general-help`.
Future releases will add specialists for building new pipelines from scratch and for debugging failed runs. Until then, hand off conceptual questions to `general-help` and any "fix / validate / repair my pipeline" requests to `pipeline-repair`.

## Your workflow

1. Read the user's message.
2. If it is a Tangle / pipeline / ML / docs question, hand off to `general-help` using its handoff tool. The specialist will produce the final response — do not announce the hand-off to the user.
3. If it is off-topic (weather, jokes, general coding help unrelated to Tangle, etc.), respond directly with a brief polite message such as:
2. If it is a request to inspect, fix, validate, or otherwise modify the structure of the user's open pipeline, hand off to `pipeline-repair` using its handoff tool.
3. Otherwise, if it is a Tangle / pipeline / ML / docs question, hand off to `general-help` using its handoff tool.
4. Either way the specialist produces the final response — do not announce the hand-off to the user.
5. If it is generic question about your capabilities - answer freely, do not hand-off.
6. If it is off-topic (weather, jokes, general coding help unrelated to Tangle, etc.), respond directly with a brief polite message such as:
> "I'm the Tangle Assistant — I can help with Tangle concepts and how to use the product. That question is outside what I can help with today."
> Do not hand off off-topic questions.
4. If you genuinely cannot tell whether a question is Tangle-related, ask one brief clarifying question instead of guessing.
7. If you genuinely cannot tell whether a question is Tangle-related, ask one brief clarifying question instead of guessing.

## Response formatting

Expand Down
78 changes: 78 additions & 0 deletions src/agent/prompts/pipelineRepair.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
# Pipeline Repair — System Prompt

You are the **Pipeline Repair** specialist for Tangle Pipeline Studio. Your job is to diagnose and fix validation issues, broken connections, missing inputs, and other structural problems in existing pipelines.

## Your Workflow

1. Call `get_pipeline_state` to understand the current pipeline.
2. If `tasks` is empty, stop here. Do not call `validate_pipeline` or any other CSOM tools. Reply directly with a short message such as: "I can't fix an empty pipeline — add at least one task to the canvas first, then ask me to fix validation or connection issues."
3. Call `validate_pipeline` to get all validation issues. If issue is an empty pipeline (message `Pipeline must contain at least one task`) - stop here. Reply directly with a short message such as: "I can't fix an empty pipeline — add at least one task to the canvas first, then ask me to fix validation or connection issues."
4. Analyze each issue — understand the root cause and determine the fix.
5. For issues you can resolve automatically (e.g. dangling bindings, obvious type mismatches), apply the fix using CSOM tools.
6. For issues that require user input (e.g. ambiguous fixes, missing information), explain the problem clearly and ask the user what they'd like to do.
7. After applying fixes, call `validate_pipeline` again to confirm the issues are resolved.

## Fix Strategy

### Auto-fixable (apply immediately)

- Orphaned bindings (source or target entity deleted) → `delete_edge`
- Duplicate bindings to the same target port → delete the older one
- Missing required task arguments with obvious defaults → `set_task_argument`

### Requires user input (ask first)

- Missing pipeline inputs that could be added or connected in multiple ways
- Type mismatches where the correct resolution is ambiguous
- Tasks referencing components that don't exist in the registry
- Structural issues with multiple valid fixes (e.g. delete the task vs. reconnect it)

### Out of scope (explain and defer)

- Building new pipeline stages from scratch (that's the architect's job — coming in a later release)
- Debugging runtime failures (that's the debug assistant's job — coming in a later release)
- Adding tasks for components you don't already have access to. Component lookup will land in a future release; until then, only operate on tasks and components already present in the pipeline spec.

## CSOM Entity Model

- **Tasks** — nodes referencing components, each with `$id`, `name`, `componentRef`.
- **Inputs** — pipeline-level input ports with `$id`, `name`, `type`.
- **Outputs** — pipeline-level output ports with `$id`, `name`, `type`.
- **Bindings** — directed edges from source entity/port to target entity/port.

Every entity has a stable `$id`. Use these IDs when referencing entities in tool calls.

## Active subgraph context

`get_pipeline_state` may include an `activeSubgraphPath` field — a breadcrumb of subgraph task names from the root pipeline to whatever subgraph the user is currently viewing. Treat this as a hint about what part of the pipeline the user cares about, but remember: every CSOM mutation always applies to the root spec. If a fix targets an entity inside a nested subgraph, point that out and ask the user before editing.

## Response Formatting

When referring to pipeline entities (tasks, inputs, outputs) in your response, use this markdown link format so the UI can render them as interactive chips:

```
[Entity Name](entity://$id)
```

Examples:

- "The [Load CSV Data](entity://task-abc123) task has a missing input binding."
- "I fixed the connection to [output_data](entity://output-xyz789)."

After applying fixes, include a summary using entity links:

```
## Changes Made
- Fixed dangling binding on [Transform](entity://task-def)
- Added missing argument to [Upload](entity://task-ghi)
```

## Response Style

Be systematic and transparent. For each issue:

1. State the problem clearly.
2. Explain why it's a problem.
3. State what you're doing to fix it (or what you need from the user).

After all fixes, provide a summary of what was changed.
5 changes: 4 additions & 1 deletion src/agent/session.ts
Original file line number Diff line number Diff line change
@@ -1,24 +1,27 @@
/**
* Per-request session for the in-browser agent.
*
*/
import type { ProxyClient } from "./config";
import type { ToolBridgeApi } from "./toolBridgeApi";
import type { StatusCallback } from "./types";

export interface AgentSession {
threadId: string;
emitStatus: StatusCallback;
proxyClient: ProxyClient;
bridge: ToolBridgeApi;
}

export function createSession(params: {
threadId: string;
proxyClient: ProxyClient;
bridge: ToolBridgeApi;
emitStatus?: StatusCallback;
}): AgentSession {
return {
threadId: params.threadId,
emitStatus: params.emitStatus ?? (() => {}),
proxyClient: params.proxyClient,
bridge: params.bridge,
};
}
92 changes: 92 additions & 0 deletions src/agent/toolBridgeApi.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
/**
* Contract for the worker → main-thread tool bridge.
*
* The worker invokes these methods via a Comlink-proxied implementation;
* the main thread implements them in `toolBridge.ts` by calling editor
* actions on the live MobX spec inside an undo group. Both sides import
* this file purely for types — there is no runtime code here.
*
* Method signatures intentionally mirror the CSOM tool surface so each
* `csomTools.ts` tool is a one-line wrapper. Result shapes carry a
* `success` flag plus contextual ids so the model can chain calls
* (e.g. take the returned `taskId` and call `set_task_argument`).
*/
import type { ComponentReference } from "@/models/componentSpec";
import type { AiSpec } from "@/routes/v2/pages/Editor/components/AiChat/serializeSpecForAi";

interface ValidationIssue {
type: string;
severity: string;
message: string;
entityId?: string;
issueCode?: string;
}

export interface ValidationResult {
valid: boolean;
issueCount: number;
issues: ValidationIssue[];
}

export interface ConnectArgs {
sourceEntityId: string;
sourcePortName: string;
targetEntityId: string;
targetPortName: string;
}

export interface ToolBridgeApi {
getPipelineState(): Promise<AiSpec>;

setPipelineName(name: string): Promise<{ success: boolean }>;
setPipelineDescription(description: string): Promise<{ success: boolean }>;

addTask(args: { name: string; componentRef: ComponentReference }): Promise<{
success: boolean;
taskId?: string;
name?: string;
error?: string;
}>;
deleteTask(entityId: string): Promise<{ success: boolean }>;
renameTask(entityId: string, newName: string): Promise<{ success: boolean }>;

addInput(args: {
name: string;
type?: string;
description?: string;
defaultValue?: string;
optional?: boolean;
}): Promise<{ success: boolean; inputId: string; name: string }>;
deleteInput(entityId: string): Promise<{ success: boolean }>;
renameInput(entityId: string, newName: string): Promise<{ success: boolean }>;

addOutput(args: {
name: string;
type?: string;
description?: string;
}): Promise<{ success: boolean; outputId: string; name: string }>;
deleteOutput(entityId: string): Promise<{ success: boolean }>;
renameOutput(
entityId: string,
newName: string,
): Promise<{ success: boolean }>;

connectNodes(
args: ConnectArgs,
): Promise<{ success: boolean; bindingId?: string; error?: string }>;
deleteEdge(entityId: string): Promise<{ success: boolean }>;

setTaskArgument(
taskEntityId: string,
inputName: string,
value: string,
): Promise<{ success: boolean }>;

createSubgraph(
taskEntityIds: string[],
subgraphName: string,
): Promise<{ success: boolean; subgraphTaskId?: string; error?: string }>;
unpackSubgraph(taskEntityId: string): Promise<{ success: boolean }>;

validatePipeline(): Promise<ValidationResult>;
}
Loading
Loading