@memberjunction/ng-task-graph-editor 0.0.0 → 6.1.0-edge.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/LICENSE +7 -0
- package/dist/lib/task-graph-canvas-adapter.d.ts +98 -0
- package/dist/lib/task-graph-canvas-adapter.d.ts.map +1 -0
- package/dist/lib/task-graph-canvas-adapter.js +244 -0
- package/dist/lib/task-graph-canvas-adapter.js.map +1 -0
- package/dist/lib/task-graph-editor-events.d.ts +137 -0
- package/dist/lib/task-graph-editor-events.d.ts.map +1 -0
- package/dist/lib/task-graph-editor-events.js +180 -0
- package/dist/lib/task-graph-editor-events.js.map +1 -0
- package/dist/lib/task-graph-editor.component.d.ts +128 -0
- package/dist/lib/task-graph-editor.component.d.ts.map +1 -0
- package/dist/lib/task-graph-editor.component.js +375 -0
- package/dist/lib/task-graph-editor.component.js.map +1 -0
- package/dist/lib/task-graph-editor.module.d.ts +13 -0
- package/dist/lib/task-graph-editor.module.d.ts.map +1 -0
- package/dist/lib/task-graph-editor.module.js +23 -0
- package/dist/lib/task-graph-editor.module.js.map +1 -0
- package/dist/lib/task-graph-properties-panel.component.d.ts +71 -0
- package/dist/lib/task-graph-properties-panel.component.d.ts.map +1 -0
- package/dist/lib/task-graph-properties-panel.component.js +238 -0
- package/dist/lib/task-graph-properties-panel.component.js.map +1 -0
- package/dist/lib/task-graph-runtime-source.d.ts +55 -0
- package/dist/lib/task-graph-runtime-source.d.ts.map +1 -0
- package/dist/lib/task-graph-runtime-source.js +66 -0
- package/dist/lib/task-graph-runtime-source.js.map +1 -0
- package/dist/public-api.d.ts +7 -0
- package/dist/public-api.d.ts.map +1 -0
- package/dist/public-api.js +15 -0
- package/dist/public-api.js.map +1 -0
- package/package.json +52 -7
- package/README.md +0 -45
package/LICENSE
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
ISC License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2023 MemberJunction
|
|
4
|
+
|
|
5
|
+
Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies.
|
|
6
|
+
|
|
7
|
+
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Projects a `TaskGraphSpec` onto the generic canvas shapes, and back.
|
|
3
|
+
*
|
|
4
|
+
* **Kept pure and separate from the component on purpose.** Everything interesting about rendering a
|
|
5
|
+
* task graph — which node is an entry point, which edge is conditional, how a runtime status maps to
|
|
6
|
+
* a visual one, whether removing a node orphans an edge — is decidable from data alone. Putting it
|
|
7
|
+
* in a component would make it reachable only through a TestBed, and the resulting tests would be
|
|
8
|
+
* about Angular rather than about graphs.
|
|
9
|
+
*
|
|
10
|
+
* **The direction flip, again.** A `TaskGraphSpec` edge points *backwards* (`dependsOn`: "I wait for
|
|
11
|
+
* X"); a canvas connection points *forwards* (source → target). Same edge, opposite authoring
|
|
12
|
+
* convention — the graph is written by whoever knows the prerequisites, the canvas drawn by whoever
|
|
13
|
+
* follows the arrows. This is the second place in the program that inversion appears (Save as
|
|
14
|
+
* Workflow is the first), which is why it is stated rather than left implicit.
|
|
15
|
+
*
|
|
16
|
+
* @module @memberjunction/ng-task-graph-editor
|
|
17
|
+
*/
|
|
18
|
+
import { type TaskGraphSpec, type TaskGraphSpecNode, type TaskGraphDependency } from '@memberjunction/ai-core-plus';
|
|
19
|
+
import type { FlowConnection, FlowNode, FlowNodeStatus, FlowNodeTypeConfig } from '@memberjunction/ng-flow-editor';
|
|
20
|
+
/** Node types the palette offers. Two, because a task is either an agent's or a person's. */
|
|
21
|
+
export declare const TASK_GRAPH_NODE_TYPES: FlowNodeTypeConfig[];
|
|
22
|
+
/** Live per-task state for the runtime overlay, keyed by `tempId`. */
|
|
23
|
+
export type TaskGraphRuntimeStatus = Record<string, TaskGraphRuntimeState>;
|
|
24
|
+
/** What a task is doing right now, in the durable engine's own vocabulary. */
|
|
25
|
+
export type TaskGraphRuntimeState = 'Pending' | 'In Progress' | 'Complete' | 'Failed' | 'Blocked' | 'Cancelled' | 'Deferred';
|
|
26
|
+
/**
|
|
27
|
+
* Maps a durable task state onto the canvas's visual vocabulary.
|
|
28
|
+
*
|
|
29
|
+
* Two mappings are worth explaining. `Blocked` renders as a **warning**, not an error: nothing went
|
|
30
|
+
* wrong, the graph simply cannot reach it — showing it as a failure would send someone hunting for a
|
|
31
|
+
* bug that does not exist. `Deferred` renders as `pending` because, to the person watching, waiting
|
|
32
|
+
* on a schedule and waiting on a prerequisite look and mean the same thing.
|
|
33
|
+
*/
|
|
34
|
+
export declare function RuntimeStateToNodeStatus(state: TaskGraphRuntimeState | undefined): FlowNodeStatus;
|
|
35
|
+
/** True when the node is a person's step rather than an agent's. */
|
|
36
|
+
export declare function IsHumanTask(node: TaskGraphSpecNode): boolean;
|
|
37
|
+
/** Entry points: nodes nothing else has to finish first. Same rule the traversal engine uses. */
|
|
38
|
+
export declare function GetEntryTempIds(spec: TaskGraphSpec): string[];
|
|
39
|
+
/** Every dependency of a node, in normalized object form. */
|
|
40
|
+
export declare function GetDependencies(node: TaskGraphSpecNode): TaskGraphDependency[];
|
|
41
|
+
/** The `tempId`s that depend on `tempId` — i.e. what breaks if it is removed. */
|
|
42
|
+
export declare function GetDependents(spec: TaskGraphSpec, tempId: string): string[];
|
|
43
|
+
/**
|
|
44
|
+
* Projects the spec onto canvas nodes.
|
|
45
|
+
*
|
|
46
|
+
* Positions are left at the origin: layout is the canvas's job (Dagre), and a spec carries no
|
|
47
|
+
* geometry precisely because a task graph is a *logical* structure. An agent that emitted one never
|
|
48
|
+
* had an opinion about where the boxes go.
|
|
49
|
+
*/
|
|
50
|
+
export declare function SpecToNodes(spec: TaskGraphSpec, runtime?: TaskGraphRuntimeStatus): FlowNode[];
|
|
51
|
+
/**
|
|
52
|
+
* Projects the spec's dependencies onto canvas connections, reversing their direction.
|
|
53
|
+
*
|
|
54
|
+
* A conditional edge is drawn dashed and labeled with its condition, because the difference between
|
|
55
|
+
* "always" and "only sometimes" is the single most consequential thing about an edge and the one a
|
|
56
|
+
* reader is most likely to miss when scanning a diagram.
|
|
57
|
+
*
|
|
58
|
+
* Edges naming a task that is not in the graph are skipped rather than drawn dangling — the
|
|
59
|
+
* validator reports them as `UnknownDependency`, and rendering a connection to nowhere would be a
|
|
60
|
+
* second, worse way of saying the same thing.
|
|
61
|
+
*/
|
|
62
|
+
export declare function SpecToConnections(spec: TaskGraphSpec): FlowConnection[];
|
|
63
|
+
/**
|
|
64
|
+
* Adds a dependency to the spec, returning a NEW spec.
|
|
65
|
+
*
|
|
66
|
+
* Immutable because the component emits the result to a host that may keep it, diff it, or reject
|
|
67
|
+
* it; mutating the input would let a canceled edit leak into the host's copy anyway. A duplicate
|
|
68
|
+
* edge is a no-op rather than an error — dragging the same connection twice is a slip, not a
|
|
69
|
+
* request to corrupt the graph.
|
|
70
|
+
*/
|
|
71
|
+
export declare function AddDependency(spec: TaskGraphSpec, fromTempId: string, toTempId: string, condition?: string): TaskGraphSpec;
|
|
72
|
+
/** Removes a dependency, returning a NEW spec. */
|
|
73
|
+
export declare function RemoveDependency(spec: TaskGraphSpec, fromTempId: string, toTempId: string): TaskGraphSpec;
|
|
74
|
+
/**
|
|
75
|
+
* Removes a task AND every edge into it, returning a NEW spec.
|
|
76
|
+
*
|
|
77
|
+
* Severing the inbound edges is not a convenience — leaving them would produce a graph whose
|
|
78
|
+
* `dependsOn` names a task that no longer exists, which the validator rejects as
|
|
79
|
+
* `UnknownDependency`. Deleting a box on a canvas should not make the graph invalid.
|
|
80
|
+
*/
|
|
81
|
+
export declare function RemoveTask(spec: TaskGraphSpec, tempId: string): TaskGraphSpec;
|
|
82
|
+
/** Adds a task, returning a NEW spec. */
|
|
83
|
+
export declare function AddTask(spec: TaskGraphSpec, task: TaskGraphSpecNode): TaskGraphSpec;
|
|
84
|
+
/** Replaces a task in place by `tempId`, returning a NEW spec. */
|
|
85
|
+
export declare function UpdateTask(spec: TaskGraphSpec, tempId: string, next: TaskGraphSpecNode): TaskGraphSpec;
|
|
86
|
+
/**
|
|
87
|
+
* Would adding `from → to` create a cycle?
|
|
88
|
+
*
|
|
89
|
+
* Answered before the edge exists, so the canvas can refuse it rather than create an invalid graph
|
|
90
|
+
* and report it afterwards. A self-edge is the degenerate case and is caught first.
|
|
91
|
+
*
|
|
92
|
+
* Iterative rather than recursive: the graph may come from an LLM, and a deeply-chained spec should
|
|
93
|
+
* produce a refusal, not a stack overflow.
|
|
94
|
+
*/
|
|
95
|
+
export declare function WouldCreateCycle(spec: TaskGraphSpec, fromTempId: string, toTempId: string): boolean;
|
|
96
|
+
/** A unique `tempId` for a newly added task, stable and readable. */
|
|
97
|
+
export declare function NextTempId(spec: TaskGraphSpec, prefix?: string): string;
|
|
98
|
+
//# sourceMappingURL=task-graph-canvas-adapter.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"task-graph-canvas-adapter.d.ts","sourceRoot":"","sources":["../../src/lib/task-graph-canvas-adapter.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AACH,OAAO,EAEH,KAAK,aAAa,EAClB,KAAK,iBAAiB,EACtB,KAAK,mBAAmB,EAC3B,MAAM,8BAA8B,CAAC;AACtC,OAAO,KAAK,EAAE,cAAc,EAAE,QAAQ,EAAE,cAAc,EAAE,kBAAkB,EAAE,MAAM,gCAAgC,CAAC;AAEnH,6FAA6F;AAC7F,eAAO,MAAM,qBAAqB,EAAE,kBAAkB,EAuBrD,CAAC;AAEF,sEAAsE;AACtE,MAAM,MAAM,sBAAsB,GAAG,MAAM,CAAC,MAAM,EAAE,qBAAqB,CAAC,CAAC;AAE3E,8EAA8E;AAC9E,MAAM,MAAM,qBAAqB,GAC3B,SAAS,GACT,aAAa,GACb,UAAU,GACV,QAAQ,GACR,SAAS,GACT,WAAW,GACX,UAAU,CAAC;AAEjB;;;;;;;GAOG;AACH,wBAAgB,wBAAwB,CAAC,KAAK,EAAE,qBAAqB,GAAG,SAAS,GAAG,cAAc,CAWjG;AAED,oEAAoE;AACpE,wBAAgB,WAAW,CAAC,IAAI,EAAE,iBAAiB,GAAG,OAAO,CAE5D;AAED,iGAAiG;AACjG,wBAAgB,eAAe,CAAC,IAAI,EAAE,aAAa,GAAG,MAAM,EAAE,CAE7D;AAED,6DAA6D;AAC7D,wBAAgB,eAAe,CAAC,IAAI,EAAE,iBAAiB,GAAG,mBAAmB,EAAE,CAE9E;AAED,iFAAiF;AACjF,wBAAgB,aAAa,CAAC,IAAI,EAAE,aAAa,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,CAI3E;AAOD;;;;;;GAMG;AACH,wBAAgB,WAAW,CAAC,IAAI,EAAE,aAAa,EAAE,OAAO,CAAC,EAAE,sBAAsB,GAAG,QAAQ,EAAE,CAsB7F;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,aAAa,GAAG,cAAc,EAAE,CA0BvE;AAED;;;;;;;GAOG;AACH,wBAAgB,aAAa,CAAC,IAAI,EAAE,aAAa,EAAE,UAAU,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,SAAS,CAAC,EAAE,MAAM,GAAG,aAAa,CAa1H;AAED,kDAAkD;AAClD,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,aAAa,EAAE,UAAU,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,aAAa,CASzG;AAED;;;;;;GAMG;AACH,wBAAgB,UAAU,CAAC,IAAI,EAAE,aAAa,EAAE,MAAM,EAAE,MAAM,GAAG,aAAa,CAO7E;AAED,yCAAyC;AACzC,wBAAgB,OAAO,CAAC,IAAI,EAAE,aAAa,EAAE,IAAI,EAAE,iBAAiB,GAAG,aAAa,CAEnF;AAED,kEAAkE;AAClE,wBAAgB,UAAU,CAAC,IAAI,EAAE,aAAa,EAAE,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,iBAAiB,GAAG,aAAa,CAEtG;AAED;;;;;;;;GAQG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,aAAa,EAAE,UAAU,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAmBnG;AAED,qEAAqE;AACrE,wBAAgB,UAAU,CAAC,IAAI,EAAE,aAAa,EAAE,MAAM,SAAS,GAAG,MAAM,CAKvE"}
|
|
@@ -0,0 +1,244 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Projects a `TaskGraphSpec` onto the generic canvas shapes, and back.
|
|
3
|
+
*
|
|
4
|
+
* **Kept pure and separate from the component on purpose.** Everything interesting about rendering a
|
|
5
|
+
* task graph — which node is an entry point, which edge is conditional, how a runtime status maps to
|
|
6
|
+
* a visual one, whether removing a node orphans an edge — is decidable from data alone. Putting it
|
|
7
|
+
* in a component would make it reachable only through a TestBed, and the resulting tests would be
|
|
8
|
+
* about Angular rather than about graphs.
|
|
9
|
+
*
|
|
10
|
+
* **The direction flip, again.** A `TaskGraphSpec` edge points *backwards* (`dependsOn`: "I wait for
|
|
11
|
+
* X"); a canvas connection points *forwards* (source → target). Same edge, opposite authoring
|
|
12
|
+
* convention — the graph is written by whoever knows the prerequisites, the canvas drawn by whoever
|
|
13
|
+
* follows the arrows. This is the second place in the program that inversion appears (Save as
|
|
14
|
+
* Workflow is the first), which is why it is stated rather than left implicit.
|
|
15
|
+
*
|
|
16
|
+
* @module @memberjunction/ng-task-graph-editor
|
|
17
|
+
*/
|
|
18
|
+
import { NormalizeDependency, } from '@memberjunction/ai-core-plus';
|
|
19
|
+
/** Node types the palette offers. Two, because a task is either an agent's or a person's. */
|
|
20
|
+
export const TASK_GRAPH_NODE_TYPES = [
|
|
21
|
+
{
|
|
22
|
+
Type: 'AgentTask',
|
|
23
|
+
Label: 'Agent Step',
|
|
24
|
+
Icon: 'fa-robot',
|
|
25
|
+
Color: '#4A6FA5',
|
|
26
|
+
Category: 'Steps',
|
|
27
|
+
DefaultPorts: [
|
|
28
|
+
{ ID: 'in', Direction: 'input', Side: 'top', Multiple: true },
|
|
29
|
+
{ ID: 'out', Direction: 'output', Side: 'bottom', Multiple: true },
|
|
30
|
+
],
|
|
31
|
+
},
|
|
32
|
+
{
|
|
33
|
+
Type: 'HumanTask',
|
|
34
|
+
Label: 'Person Step',
|
|
35
|
+
Icon: 'fa-user-check',
|
|
36
|
+
Color: '#7B1FA2',
|
|
37
|
+
Category: 'Steps',
|
|
38
|
+
DefaultPorts: [
|
|
39
|
+
{ ID: 'in', Direction: 'input', Side: 'top', Multiple: true },
|
|
40
|
+
{ ID: 'out', Direction: 'output', Side: 'bottom', Multiple: true },
|
|
41
|
+
],
|
|
42
|
+
},
|
|
43
|
+
];
|
|
44
|
+
/**
|
|
45
|
+
* Maps a durable task state onto the canvas's visual vocabulary.
|
|
46
|
+
*
|
|
47
|
+
* Two mappings are worth explaining. `Blocked` renders as a **warning**, not an error: nothing went
|
|
48
|
+
* wrong, the graph simply cannot reach it — showing it as a failure would send someone hunting for a
|
|
49
|
+
* bug that does not exist. `Deferred` renders as `pending` because, to the person watching, waiting
|
|
50
|
+
* on a schedule and waiting on a prerequisite look and mean the same thing.
|
|
51
|
+
*/
|
|
52
|
+
export function RuntimeStateToNodeStatus(state) {
|
|
53
|
+
switch (state) {
|
|
54
|
+
case 'In Progress': return 'running';
|
|
55
|
+
case 'Complete': return 'success';
|
|
56
|
+
case 'Failed': return 'error';
|
|
57
|
+
case 'Blocked': return 'warning';
|
|
58
|
+
case 'Cancelled': return 'disabled';
|
|
59
|
+
case 'Deferred': return 'pending';
|
|
60
|
+
case 'Pending': return 'pending';
|
|
61
|
+
default: return 'default';
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
/** True when the node is a person's step rather than an agent's. */
|
|
65
|
+
export function IsHumanTask(node) {
|
|
66
|
+
return !node.agentName;
|
|
67
|
+
}
|
|
68
|
+
/** Entry points: nodes nothing else has to finish first. Same rule the traversal engine uses. */
|
|
69
|
+
export function GetEntryTempIds(spec) {
|
|
70
|
+
return (spec.tasks ?? []).filter((t) => (t.dependsOn ?? []).length === 0).map((t) => t.tempId);
|
|
71
|
+
}
|
|
72
|
+
/** Every dependency of a node, in normalized object form. */
|
|
73
|
+
export function GetDependencies(node) {
|
|
74
|
+
return (node.dependsOn ?? []).map(NormalizeDependency);
|
|
75
|
+
}
|
|
76
|
+
/** The `tempId`s that depend on `tempId` — i.e. what breaks if it is removed. */
|
|
77
|
+
export function GetDependents(spec, tempId) {
|
|
78
|
+
return (spec.tasks ?? [])
|
|
79
|
+
.filter((t) => GetDependencies(t).some((d) => d.tempId === tempId))
|
|
80
|
+
.map((t) => t.tempId);
|
|
81
|
+
}
|
|
82
|
+
/** Deterministic id for a canvas edge, so re-renders don't churn selection. */
|
|
83
|
+
function edgeId(fromTempId, toTempId) {
|
|
84
|
+
return `${fromTempId}->${toTempId}`;
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* Projects the spec onto canvas nodes.
|
|
88
|
+
*
|
|
89
|
+
* Positions are left at the origin: layout is the canvas's job (Dagre), and a spec carries no
|
|
90
|
+
* geometry precisely because a task graph is a *logical* structure. An agent that emitted one never
|
|
91
|
+
* had an opinion about where the boxes go.
|
|
92
|
+
*/
|
|
93
|
+
export function SpecToNodes(spec, runtime) {
|
|
94
|
+
const entryIds = new Set(GetEntryTempIds(spec));
|
|
95
|
+
return (spec.tasks ?? []).map((task) => {
|
|
96
|
+
const human = IsHumanTask(task);
|
|
97
|
+
return {
|
|
98
|
+
ID: task.tempId,
|
|
99
|
+
Type: human ? 'HumanTask' : 'AgentTask',
|
|
100
|
+
Label: task.name,
|
|
101
|
+
Subtitle: human ? 'Waiting on a person' : task.agentName,
|
|
102
|
+
Icon: human ? 'fa-user-check' : 'fa-robot',
|
|
103
|
+
Status: RuntimeStateToNodeStatus(runtime?.[task.tempId]),
|
|
104
|
+
StatusMessage: task.description,
|
|
105
|
+
IsStartNode: entryIds.has(task.tempId),
|
|
106
|
+
Position: { X: 0, Y: 0 },
|
|
107
|
+
Ports: [
|
|
108
|
+
{ ID: 'in', Direction: 'input', Side: 'top', Multiple: true },
|
|
109
|
+
{ ID: 'out', Direction: 'output', Side: 'bottom', Multiple: true },
|
|
110
|
+
],
|
|
111
|
+
Data: { TempId: task.tempId },
|
|
112
|
+
};
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* Projects the spec's dependencies onto canvas connections, reversing their direction.
|
|
117
|
+
*
|
|
118
|
+
* A conditional edge is drawn dashed and labeled with its condition, because the difference between
|
|
119
|
+
* "always" and "only sometimes" is the single most consequential thing about an edge and the one a
|
|
120
|
+
* reader is most likely to miss when scanning a diagram.
|
|
121
|
+
*
|
|
122
|
+
* Edges naming a task that is not in the graph are skipped rather than drawn dangling — the
|
|
123
|
+
* validator reports them as `UnknownDependency`, and rendering a connection to nowhere would be a
|
|
124
|
+
* second, worse way of saying the same thing.
|
|
125
|
+
*/
|
|
126
|
+
export function SpecToConnections(spec) {
|
|
127
|
+
const known = new Set((spec.tasks ?? []).map((t) => t.tempId));
|
|
128
|
+
const connections = [];
|
|
129
|
+
for (const task of spec.tasks ?? []) {
|
|
130
|
+
for (const dep of GetDependencies(task)) {
|
|
131
|
+
if (!known.has(dep.tempId))
|
|
132
|
+
continue;
|
|
133
|
+
const conditional = !!dep.condition?.trim();
|
|
134
|
+
connections.push({
|
|
135
|
+
ID: edgeId(dep.tempId, task.tempId),
|
|
136
|
+
// Reversed: dependsOn points back at the prerequisite, the drawn arrow points forward.
|
|
137
|
+
SourceNodeID: dep.tempId,
|
|
138
|
+
SourcePortID: 'out',
|
|
139
|
+
TargetNodeID: task.tempId,
|
|
140
|
+
TargetPortID: 'in',
|
|
141
|
+
Label: conditional ? 'if' : undefined,
|
|
142
|
+
LabelDetail: dep.condition ?? undefined,
|
|
143
|
+
LabelIcon: conditional ? 'fa-code-branch' : undefined,
|
|
144
|
+
Condition: dep.condition ?? undefined,
|
|
145
|
+
Style: conditional ? 'dashed' : 'solid',
|
|
146
|
+
Data: { FromTempId: dep.tempId, ToTempId: task.tempId },
|
|
147
|
+
});
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
return connections;
|
|
151
|
+
}
|
|
152
|
+
/**
|
|
153
|
+
* Adds a dependency to the spec, returning a NEW spec.
|
|
154
|
+
*
|
|
155
|
+
* Immutable because the component emits the result to a host that may keep it, diff it, or reject
|
|
156
|
+
* it; mutating the input would let a canceled edit leak into the host's copy anyway. A duplicate
|
|
157
|
+
* edge is a no-op rather than an error — dragging the same connection twice is a slip, not a
|
|
158
|
+
* request to corrupt the graph.
|
|
159
|
+
*/
|
|
160
|
+
export function AddDependency(spec, fromTempId, toTempId, condition) {
|
|
161
|
+
return {
|
|
162
|
+
...spec,
|
|
163
|
+
tasks: (spec.tasks ?? []).map((task) => {
|
|
164
|
+
if (task.tempId !== toTempId)
|
|
165
|
+
return task;
|
|
166
|
+
const existing = GetDependencies(task);
|
|
167
|
+
if (existing.some((d) => d.tempId === fromTempId))
|
|
168
|
+
return task;
|
|
169
|
+
const next = condition?.trim()
|
|
170
|
+
? { tempId: fromTempId, condition }
|
|
171
|
+
: fromTempId;
|
|
172
|
+
return { ...task, dependsOn: [...(task.dependsOn ?? []), next] };
|
|
173
|
+
}),
|
|
174
|
+
};
|
|
175
|
+
}
|
|
176
|
+
/** Removes a dependency, returning a NEW spec. */
|
|
177
|
+
export function RemoveDependency(spec, fromTempId, toTempId) {
|
|
178
|
+
return {
|
|
179
|
+
...spec,
|
|
180
|
+
tasks: (spec.tasks ?? []).map((task) => task.tempId !== toTempId
|
|
181
|
+
? task
|
|
182
|
+
: { ...task, dependsOn: (task.dependsOn ?? []).filter((d) => NormalizeDependency(d).tempId !== fromTempId) }),
|
|
183
|
+
};
|
|
184
|
+
}
|
|
185
|
+
/**
|
|
186
|
+
* Removes a task AND every edge into it, returning a NEW spec.
|
|
187
|
+
*
|
|
188
|
+
* Severing the inbound edges is not a convenience — leaving them would produce a graph whose
|
|
189
|
+
* `dependsOn` names a task that no longer exists, which the validator rejects as
|
|
190
|
+
* `UnknownDependency`. Deleting a box on a canvas should not make the graph invalid.
|
|
191
|
+
*/
|
|
192
|
+
export function RemoveTask(spec, tempId) {
|
|
193
|
+
return {
|
|
194
|
+
...spec,
|
|
195
|
+
tasks: (spec.tasks ?? [])
|
|
196
|
+
.filter((t) => t.tempId !== tempId)
|
|
197
|
+
.map((t) => ({ ...t, dependsOn: (t.dependsOn ?? []).filter((d) => NormalizeDependency(d).tempId !== tempId) })),
|
|
198
|
+
};
|
|
199
|
+
}
|
|
200
|
+
/** Adds a task, returning a NEW spec. */
|
|
201
|
+
export function AddTask(spec, task) {
|
|
202
|
+
return { ...spec, tasks: [...(spec.tasks ?? []), task] };
|
|
203
|
+
}
|
|
204
|
+
/** Replaces a task in place by `tempId`, returning a NEW spec. */
|
|
205
|
+
export function UpdateTask(spec, tempId, next) {
|
|
206
|
+
return { ...spec, tasks: (spec.tasks ?? []).map((t) => (t.tempId === tempId ? next : t)) };
|
|
207
|
+
}
|
|
208
|
+
/**
|
|
209
|
+
* Would adding `from → to` create a cycle?
|
|
210
|
+
*
|
|
211
|
+
* Answered before the edge exists, so the canvas can refuse it rather than create an invalid graph
|
|
212
|
+
* and report it afterwards. A self-edge is the degenerate case and is caught first.
|
|
213
|
+
*
|
|
214
|
+
* Iterative rather than recursive: the graph may come from an LLM, and a deeply-chained spec should
|
|
215
|
+
* produce a refusal, not a stack overflow.
|
|
216
|
+
*/
|
|
217
|
+
export function WouldCreateCycle(spec, fromTempId, toTempId) {
|
|
218
|
+
if (fromTempId === toTempId)
|
|
219
|
+
return true;
|
|
220
|
+
// A cycle appears iff `from` is already reachable from `to` by following dependsOn edges —
|
|
221
|
+
// i.e. `to` already (transitively) waits on `from`.
|
|
222
|
+
const dependenciesOf = new Map((spec.tasks ?? []).map((t) => [t.tempId, GetDependencies(t).map((d) => d.tempId)]));
|
|
223
|
+
const stack = [...(dependenciesOf.get(fromTempId) ?? [])];
|
|
224
|
+
const seen = new Set();
|
|
225
|
+
while (stack.length > 0) {
|
|
226
|
+
const current = stack.pop();
|
|
227
|
+
if (current === toTempId)
|
|
228
|
+
return true;
|
|
229
|
+
if (seen.has(current))
|
|
230
|
+
continue;
|
|
231
|
+
seen.add(current);
|
|
232
|
+
stack.push(...(dependenciesOf.get(current) ?? []));
|
|
233
|
+
}
|
|
234
|
+
return false;
|
|
235
|
+
}
|
|
236
|
+
/** A unique `tempId` for a newly added task, stable and readable. */
|
|
237
|
+
export function NextTempId(spec, prefix = 'task') {
|
|
238
|
+
const taken = new Set((spec.tasks ?? []).map((t) => t.tempId));
|
|
239
|
+
let n = (spec.tasks ?? []).length + 1;
|
|
240
|
+
while (taken.has(`${prefix}${n}`))
|
|
241
|
+
n++;
|
|
242
|
+
return `${prefix}${n}`;
|
|
243
|
+
}
|
|
244
|
+
//# sourceMappingURL=task-graph-canvas-adapter.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"task-graph-canvas-adapter.js","sourceRoot":"","sources":["../../src/lib/task-graph-canvas-adapter.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * @fileoverview Projects a `TaskGraphSpec` onto the generic canvas shapes, and back.\n *\n * **Kept pure and separate from the component on purpose.** Everything interesting about rendering a\n * task graph — which node is an entry point, which edge is conditional, how a runtime status maps to\n * a visual one, whether removing a node orphans an edge — is decidable from data alone. Putting it\n * in a component would make it reachable only through a TestBed, and the resulting tests would be\n * about Angular rather than about graphs.\n *\n * **The direction flip, again.** A `TaskGraphSpec` edge points *backwards* (`dependsOn`: \"I wait for\n * X\"); a canvas connection points *forwards* (source → target). Same edge, opposite authoring\n * convention — the graph is written by whoever knows the prerequisites, the canvas drawn by whoever\n * follows the arrows. This is the second place in the program that inversion appears (Save as\n * Workflow is the first), which is why it is stated rather than left implicit.\n *\n * @module @memberjunction/ng-task-graph-editor\n */\nimport {\n NormalizeDependency,\n type TaskGraphSpec,\n type TaskGraphSpecNode,\n type TaskGraphDependency,\n} from '@memberjunction/ai-core-plus';\nimport type { FlowConnection, FlowNode, FlowNodeStatus, FlowNodeTypeConfig } from '@memberjunction/ng-flow-editor';\n\n/** Node types the palette offers. Two, because a task is either an agent's or a person's. */\nexport const TASK_GRAPH_NODE_TYPES: FlowNodeTypeConfig[] = [\n {\n Type: 'AgentTask',\n Label: 'Agent Step',\n Icon: 'fa-robot',\n Color: '#4A6FA5',\n Category: 'Steps',\n DefaultPorts: [\n { ID: 'in', Direction: 'input', Side: 'top', Multiple: true },\n { ID: 'out', Direction: 'output', Side: 'bottom', Multiple: true },\n ],\n },\n {\n Type: 'HumanTask',\n Label: 'Person Step',\n Icon: 'fa-user-check',\n Color: '#7B1FA2',\n Category: 'Steps',\n DefaultPorts: [\n { ID: 'in', Direction: 'input', Side: 'top', Multiple: true },\n { ID: 'out', Direction: 'output', Side: 'bottom', Multiple: true },\n ],\n },\n];\n\n/** Live per-task state for the runtime overlay, keyed by `tempId`. */\nexport type TaskGraphRuntimeStatus = Record<string, TaskGraphRuntimeState>;\n\n/** What a task is doing right now, in the durable engine's own vocabulary. */\nexport type TaskGraphRuntimeState =\n | 'Pending'\n | 'In Progress'\n | 'Complete'\n | 'Failed'\n | 'Blocked'\n | 'Cancelled'\n | 'Deferred';\n\n/**\n * Maps a durable task state onto the canvas's visual vocabulary.\n *\n * Two mappings are worth explaining. `Blocked` renders as a **warning**, not an error: nothing went\n * wrong, the graph simply cannot reach it — showing it as a failure would send someone hunting for a\n * bug that does not exist. `Deferred` renders as `pending` because, to the person watching, waiting\n * on a schedule and waiting on a prerequisite look and mean the same thing.\n */\nexport function RuntimeStateToNodeStatus(state: TaskGraphRuntimeState | undefined): FlowNodeStatus {\n switch (state) {\n case 'In Progress': return 'running';\n case 'Complete': return 'success';\n case 'Failed': return 'error';\n case 'Blocked': return 'warning';\n case 'Cancelled': return 'disabled';\n case 'Deferred': return 'pending';\n case 'Pending': return 'pending';\n default: return 'default';\n }\n}\n\n/** True when the node is a person's step rather than an agent's. */\nexport function IsHumanTask(node: TaskGraphSpecNode): boolean {\n return !node.agentName;\n}\n\n/** Entry points: nodes nothing else has to finish first. Same rule the traversal engine uses. */\nexport function GetEntryTempIds(spec: TaskGraphSpec): string[] {\n return (spec.tasks ?? []).filter((t) => (t.dependsOn ?? []).length === 0).map((t) => t.tempId);\n}\n\n/** Every dependency of a node, in normalized object form. */\nexport function GetDependencies(node: TaskGraphSpecNode): TaskGraphDependency[] {\n return (node.dependsOn ?? []).map(NormalizeDependency);\n}\n\n/** The `tempId`s that depend on `tempId` — i.e. what breaks if it is removed. */\nexport function GetDependents(spec: TaskGraphSpec, tempId: string): string[] {\n return (spec.tasks ?? [])\n .filter((t) => GetDependencies(t).some((d) => d.tempId === tempId))\n .map((t) => t.tempId);\n}\n\n/** Deterministic id for a canvas edge, so re-renders don't churn selection. */\nfunction edgeId(fromTempId: string, toTempId: string): string {\n return `${fromTempId}->${toTempId}`;\n}\n\n/**\n * Projects the spec onto canvas nodes.\n *\n * Positions are left at the origin: layout is the canvas's job (Dagre), and a spec carries no\n * geometry precisely because a task graph is a *logical* structure. An agent that emitted one never\n * had an opinion about where the boxes go.\n */\nexport function SpecToNodes(spec: TaskGraphSpec, runtime?: TaskGraphRuntimeStatus): FlowNode[] {\n const entryIds = new Set(GetEntryTempIds(spec));\n\n return (spec.tasks ?? []).map((task) => {\n const human = IsHumanTask(task);\n return {\n ID: task.tempId,\n Type: human ? 'HumanTask' : 'AgentTask',\n Label: task.name,\n Subtitle: human ? 'Waiting on a person' : task.agentName,\n Icon: human ? 'fa-user-check' : 'fa-robot',\n Status: RuntimeStateToNodeStatus(runtime?.[task.tempId]),\n StatusMessage: task.description,\n IsStartNode: entryIds.has(task.tempId),\n Position: { X: 0, Y: 0 },\n Ports: [\n { ID: 'in', Direction: 'input', Side: 'top', Multiple: true },\n { ID: 'out', Direction: 'output', Side: 'bottom', Multiple: true },\n ],\n Data: { TempId: task.tempId },\n };\n });\n}\n\n/**\n * Projects the spec's dependencies onto canvas connections, reversing their direction.\n *\n * A conditional edge is drawn dashed and labeled with its condition, because the difference between\n * \"always\" and \"only sometimes\" is the single most consequential thing about an edge and the one a\n * reader is most likely to miss when scanning a diagram.\n *\n * Edges naming a task that is not in the graph are skipped rather than drawn dangling — the\n * validator reports them as `UnknownDependency`, and rendering a connection to nowhere would be a\n * second, worse way of saying the same thing.\n */\nexport function SpecToConnections(spec: TaskGraphSpec): FlowConnection[] {\n const known = new Set((spec.tasks ?? []).map((t) => t.tempId));\n const connections: FlowConnection[] = [];\n\n for (const task of spec.tasks ?? []) {\n for (const dep of GetDependencies(task)) {\n if (!known.has(dep.tempId)) continue;\n\n const conditional = !!dep.condition?.trim();\n connections.push({\n ID: edgeId(dep.tempId, task.tempId),\n // Reversed: dependsOn points back at the prerequisite, the drawn arrow points forward.\n SourceNodeID: dep.tempId,\n SourcePortID: 'out',\n TargetNodeID: task.tempId,\n TargetPortID: 'in',\n Label: conditional ? 'if' : undefined,\n LabelDetail: dep.condition ?? undefined,\n LabelIcon: conditional ? 'fa-code-branch' : undefined,\n Condition: dep.condition ?? undefined,\n Style: conditional ? 'dashed' : 'solid',\n Data: { FromTempId: dep.tempId, ToTempId: task.tempId },\n });\n }\n }\n return connections;\n}\n\n/**\n * Adds a dependency to the spec, returning a NEW spec.\n *\n * Immutable because the component emits the result to a host that may keep it, diff it, or reject\n * it; mutating the input would let a canceled edit leak into the host's copy anyway. A duplicate\n * edge is a no-op rather than an error — dragging the same connection twice is a slip, not a\n * request to corrupt the graph.\n */\nexport function AddDependency(spec: TaskGraphSpec, fromTempId: string, toTempId: string, condition?: string): TaskGraphSpec {\n return {\n ...spec,\n tasks: (spec.tasks ?? []).map((task) => {\n if (task.tempId !== toTempId) return task;\n const existing = GetDependencies(task);\n if (existing.some((d) => d.tempId === fromTempId)) return task;\n const next: TaskGraphDependency | string = condition?.trim()\n ? { tempId: fromTempId, condition }\n : fromTempId;\n return { ...task, dependsOn: [...(task.dependsOn ?? []), next] };\n }),\n };\n}\n\n/** Removes a dependency, returning a NEW spec. */\nexport function RemoveDependency(spec: TaskGraphSpec, fromTempId: string, toTempId: string): TaskGraphSpec {\n return {\n ...spec,\n tasks: (spec.tasks ?? []).map((task) =>\n task.tempId !== toTempId\n ? task\n : { ...task, dependsOn: (task.dependsOn ?? []).filter((d) => NormalizeDependency(d).tempId !== fromTempId) },\n ),\n };\n}\n\n/**\n * Removes a task AND every edge into it, returning a NEW spec.\n *\n * Severing the inbound edges is not a convenience — leaving them would produce a graph whose\n * `dependsOn` names a task that no longer exists, which the validator rejects as\n * `UnknownDependency`. Deleting a box on a canvas should not make the graph invalid.\n */\nexport function RemoveTask(spec: TaskGraphSpec, tempId: string): TaskGraphSpec {\n return {\n ...spec,\n tasks: (spec.tasks ?? [])\n .filter((t) => t.tempId !== tempId)\n .map((t) => ({ ...t, dependsOn: (t.dependsOn ?? []).filter((d) => NormalizeDependency(d).tempId !== tempId) })),\n };\n}\n\n/** Adds a task, returning a NEW spec. */\nexport function AddTask(spec: TaskGraphSpec, task: TaskGraphSpecNode): TaskGraphSpec {\n return { ...spec, tasks: [...(spec.tasks ?? []), task] };\n}\n\n/** Replaces a task in place by `tempId`, returning a NEW spec. */\nexport function UpdateTask(spec: TaskGraphSpec, tempId: string, next: TaskGraphSpecNode): TaskGraphSpec {\n return { ...spec, tasks: (spec.tasks ?? []).map((t) => (t.tempId === tempId ? next : t)) };\n}\n\n/**\n * Would adding `from → to` create a cycle?\n *\n * Answered before the edge exists, so the canvas can refuse it rather than create an invalid graph\n * and report it afterwards. A self-edge is the degenerate case and is caught first.\n *\n * Iterative rather than recursive: the graph may come from an LLM, and a deeply-chained spec should\n * produce a refusal, not a stack overflow.\n */\nexport function WouldCreateCycle(spec: TaskGraphSpec, fromTempId: string, toTempId: string): boolean {\n if (fromTempId === toTempId) return true;\n\n // A cycle appears iff `from` is already reachable from `to` by following dependsOn edges —\n // i.e. `to` already (transitively) waits on `from`.\n const dependenciesOf = new Map<string, string[]>(\n (spec.tasks ?? []).map((t) => [t.tempId, GetDependencies(t).map((d) => d.tempId)]),\n );\n\n const stack = [...(dependenciesOf.get(fromTempId) ?? [])];\n const seen = new Set<string>();\n while (stack.length > 0) {\n const current = stack.pop()!;\n if (current === toTempId) return true;\n if (seen.has(current)) continue;\n seen.add(current);\n stack.push(...(dependenciesOf.get(current) ?? []));\n }\n return false;\n}\n\n/** A unique `tempId` for a newly added task, stable and readable. */\nexport function NextTempId(spec: TaskGraphSpec, prefix = 'task'): string {\n const taken = new Set((spec.tasks ?? []).map((t) => t.tempId));\n let n = (spec.tasks ?? []).length + 1;\n while (taken.has(`${prefix}${n}`)) n++;\n return `${prefix}${n}`;\n}\n"]}
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview The component's upward contract.
|
|
3
|
+
*
|
|
4
|
+
* Follows MJ's `Before*` / `After*` cancelable pattern (see `guides/UI_LAYERING_GUIDE.md` §6),
|
|
5
|
+
* already used by `ng-base-forms`, `ng-trees`, `ng-entity-viewer` and `ng-conversations`.
|
|
6
|
+
*
|
|
7
|
+
* **Why the args are classes and not payload interfaces.** `Cancel` has to travel *back*. Angular's
|
|
8
|
+
* `EventEmitter` is synchronous for synchronous listeners, so the emitting component reads the
|
|
9
|
+
* mutated object after `.emit()` returns — that is the entire veto mechanism. A frozen payload
|
|
10
|
+
* cannot carry a veto, and a class additionally gives one place for defaults and future fields.
|
|
11
|
+
*
|
|
12
|
+
* **A `Before*` handler must not be `async`.** An `await` inside it returns control to the emitter
|
|
13
|
+
* before the handler sets `Cancel`, so the veto silently does nothing. Where a host genuinely needs
|
|
14
|
+
* to await — a confirmation dialog — the component exposes the action as a public method the host
|
|
15
|
+
* calls *after* its own await, rather than pretending the veto is asynchronous.
|
|
16
|
+
*
|
|
17
|
+
* @module @memberjunction/ng-task-graph-editor
|
|
18
|
+
*/
|
|
19
|
+
import type { TaskGraphSpec, TaskGraphSpecNode, TaskGraphValidationError } from '@memberjunction/ai-core-plus';
|
|
20
|
+
/**
|
|
21
|
+
* Base for every cancelable graph event.
|
|
22
|
+
*
|
|
23
|
+
* A listener flips `Cancel = true` to halt the default behavior; the matching `After*` event will
|
|
24
|
+
* then **not** fire. That is a contract hosts rely on, not a suggestion.
|
|
25
|
+
*/
|
|
26
|
+
export declare class CancellableTaskGraphEventArgs {
|
|
27
|
+
Cancel: boolean;
|
|
28
|
+
CancelReason?: string;
|
|
29
|
+
}
|
|
30
|
+
/** Fired BEFORE a task is added. Cancel to block it (e.g. the host caps graph size). */
|
|
31
|
+
export declare class BeforeTaskAddedEventArgs extends CancellableTaskGraphEventArgs {
|
|
32
|
+
readonly Task: TaskGraphSpecNode;
|
|
33
|
+
constructor(Task: TaskGraphSpecNode);
|
|
34
|
+
}
|
|
35
|
+
/** Fired AFTER a task was added. NOT fired when the Before was canceled. */
|
|
36
|
+
export declare class AfterTaskAddedEventArgs {
|
|
37
|
+
readonly Task: TaskGraphSpecNode;
|
|
38
|
+
readonly Spec: TaskGraphSpec;
|
|
39
|
+
constructor(Task: TaskGraphSpecNode, Spec: TaskGraphSpec);
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Fired BEFORE a task is removed. Cancel to block it.
|
|
43
|
+
*
|
|
44
|
+
* `DependentTempIds` is supplied because removing a node also severs every edge into it, and a host
|
|
45
|
+
* deciding whether to veto needs to know the blast radius — not just which node was clicked.
|
|
46
|
+
*/
|
|
47
|
+
export declare class BeforeTaskRemovedEventArgs extends CancellableTaskGraphEventArgs {
|
|
48
|
+
readonly Task: TaskGraphSpecNode;
|
|
49
|
+
readonly DependentTempIds: readonly string[];
|
|
50
|
+
constructor(Task: TaskGraphSpecNode, DependentTempIds: readonly string[]);
|
|
51
|
+
}
|
|
52
|
+
/** Fired AFTER a task was removed. NOT fired when the Before was canceled. */
|
|
53
|
+
export declare class AfterTaskRemovedEventArgs {
|
|
54
|
+
readonly Task: TaskGraphSpecNode;
|
|
55
|
+
readonly Spec: TaskGraphSpec;
|
|
56
|
+
constructor(Task: TaskGraphSpecNode, Spec: TaskGraphSpec);
|
|
57
|
+
}
|
|
58
|
+
/** Fired BEFORE a task's properties change. Cancel to block the edit. */
|
|
59
|
+
export declare class BeforeTaskUpdatedEventArgs extends CancellableTaskGraphEventArgs {
|
|
60
|
+
readonly Previous: TaskGraphSpecNode;
|
|
61
|
+
readonly Next: TaskGraphSpecNode;
|
|
62
|
+
constructor(Previous: TaskGraphSpecNode, Next: TaskGraphSpecNode);
|
|
63
|
+
}
|
|
64
|
+
/** Fired AFTER a task's properties changed. NOT fired when the Before was canceled. */
|
|
65
|
+
export declare class AfterTaskUpdatedEventArgs {
|
|
66
|
+
readonly Task: TaskGraphSpecNode;
|
|
67
|
+
readonly Spec: TaskGraphSpec;
|
|
68
|
+
constructor(Task: TaskGraphSpecNode, Spec: TaskGraphSpec);
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Fired BEFORE a dependency edge is created. Cancel to block it.
|
|
72
|
+
*
|
|
73
|
+
* `WouldCreateCycle` is computed before emitting so a host can veto on that alone without
|
|
74
|
+
* re-deriving it. The component refuses a cycle regardless — the flag exists so the host can
|
|
75
|
+
* explain *why* rather than watch an edge silently fail to appear.
|
|
76
|
+
*/
|
|
77
|
+
export declare class BeforeDependencyAddedEventArgs extends CancellableTaskGraphEventArgs {
|
|
78
|
+
readonly FromTempId: string;
|
|
79
|
+
readonly ToTempId: string;
|
|
80
|
+
readonly WouldCreateCycle: boolean;
|
|
81
|
+
constructor(FromTempId: string, ToTempId: string, WouldCreateCycle: boolean);
|
|
82
|
+
}
|
|
83
|
+
/** Fired AFTER a dependency edge was created. NOT fired when the Before was canceled. */
|
|
84
|
+
export declare class AfterDependencyAddedEventArgs {
|
|
85
|
+
readonly FromTempId: string;
|
|
86
|
+
readonly ToTempId: string;
|
|
87
|
+
readonly Spec: TaskGraphSpec;
|
|
88
|
+
constructor(FromTempId: string, ToTempId: string, Spec: TaskGraphSpec);
|
|
89
|
+
}
|
|
90
|
+
/** Fired BEFORE a dependency edge is removed. Cancel to block it. */
|
|
91
|
+
export declare class BeforeDependencyRemovedEventArgs extends CancellableTaskGraphEventArgs {
|
|
92
|
+
readonly FromTempId: string;
|
|
93
|
+
readonly ToTempId: string;
|
|
94
|
+
constructor(FromTempId: string, ToTempId: string);
|
|
95
|
+
}
|
|
96
|
+
/** Fired AFTER a dependency edge was removed. NOT fired when the Before was canceled. */
|
|
97
|
+
export declare class AfterDependencyRemovedEventArgs {
|
|
98
|
+
readonly FromTempId: string;
|
|
99
|
+
readonly ToTempId: string;
|
|
100
|
+
readonly Spec: TaskGraphSpec;
|
|
101
|
+
constructor(FromTempId: string, ToTempId: string, Spec: TaskGraphSpec);
|
|
102
|
+
}
|
|
103
|
+
/** The spec changed for any reason. Hosts persist from here. */
|
|
104
|
+
export declare class TaskGraphSpecChangedEventArgs {
|
|
105
|
+
readonly Spec: TaskGraphSpec;
|
|
106
|
+
readonly Reason: 'TaskAdded' | 'TaskRemoved' | 'TaskUpdated' | 'DependencyAdded' | 'DependencyRemoved' | 'LayoutApplied';
|
|
107
|
+
constructor(Spec: TaskGraphSpec, Reason: 'TaskAdded' | 'TaskRemoved' | 'TaskUpdated' | 'DependencyAdded' | 'DependencyRemoved' | 'LayoutApplied');
|
|
108
|
+
}
|
|
109
|
+
/** Selection changed. `Task` is null when the selection was cleared. */
|
|
110
|
+
export declare class TaskGraphSelectionChangedEventArgs {
|
|
111
|
+
readonly Task: TaskGraphSpecNode | null;
|
|
112
|
+
constructor(Task: TaskGraphSpecNode | null);
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* Validation ran.
|
|
116
|
+
*
|
|
117
|
+
* Errors come from the engine's own `ValidateTaskGraphSpec`, not from a second implementation here —
|
|
118
|
+
* so a graph that looks valid on the canvas cannot fail a different check at submission.
|
|
119
|
+
*/
|
|
120
|
+
export declare class TaskGraphValidationChangedEventArgs {
|
|
121
|
+
readonly Valid: boolean;
|
|
122
|
+
readonly Errors: readonly TaskGraphValidationError[];
|
|
123
|
+
constructor(Valid: boolean, Errors: readonly TaskGraphValidationError[]);
|
|
124
|
+
}
|
|
125
|
+
/** The user asked to open the agent behind a task. The host navigates. */
|
|
126
|
+
export declare class AgentOpenRequestedEventArgs {
|
|
127
|
+
readonly AgentName: string;
|
|
128
|
+
readonly Task: TaskGraphSpecNode;
|
|
129
|
+
constructor(AgentName: string, Task: TaskGraphSpecNode);
|
|
130
|
+
}
|
|
131
|
+
/** The user asked to open a record referenced by the graph. The host navigates. */
|
|
132
|
+
export declare class RecordOpenRequestedEventArgs {
|
|
133
|
+
readonly EntityName: string;
|
|
134
|
+
readonly RecordID: string;
|
|
135
|
+
constructor(EntityName: string, RecordID: string);
|
|
136
|
+
}
|
|
137
|
+
//# sourceMappingURL=task-graph-editor-events.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"task-graph-editor-events.d.ts","sourceRoot":"","sources":["../../src/lib/task-graph-editor-events.ts"],"names":[],"mappings":"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"}
|