@memberjunction/task-graph 0.0.0 → 6.1.0-edge.2

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.
Files changed (51) hide show
  1. package/LICENSE +7 -0
  2. package/README.md +167 -27
  3. package/dist/DispatcherConditionEvaluator.d.ts +10 -0
  4. package/dist/DispatcherConditionEvaluator.d.ts.map +1 -0
  5. package/dist/DispatcherConditionEvaluator.js +27 -0
  6. package/dist/DispatcherConditionEvaluator.js.map +1 -0
  7. package/dist/TaskClaimStore.d.ts +125 -0
  8. package/dist/TaskClaimStore.d.ts.map +1 -0
  9. package/dist/TaskClaimStore.js +223 -0
  10. package/dist/TaskClaimStore.js.map +1 -0
  11. package/dist/TaskGraphDispatcher.d.ts +548 -0
  12. package/dist/TaskGraphDispatcher.d.ts.map +1 -0
  13. package/dist/TaskGraphDispatcher.js +2232 -0
  14. package/dist/TaskGraphDispatcher.js.map +1 -0
  15. package/dist/TaskGraphService.d.ts +247 -0
  16. package/dist/TaskGraphService.d.ts.map +1 -0
  17. package/dist/TaskGraphService.js +753 -0
  18. package/dist/TaskGraphService.js.map +1 -0
  19. package/dist/TaskGraphSubmitterImpl.d.ts +7 -0
  20. package/dist/TaskGraphSubmitterImpl.d.ts.map +1 -0
  21. package/dist/TaskGraphSubmitterImpl.js +52 -0
  22. package/dist/TaskGraphSubmitterImpl.js.map +1 -0
  23. package/dist/TaskLoopExecutor.d.ts +62 -0
  24. package/dist/TaskLoopExecutor.d.ts.map +1 -0
  25. package/dist/TaskLoopExecutor.js +248 -0
  26. package/dist/TaskLoopExecutor.js.map +1 -0
  27. package/dist/WorkflowSpecSync.d.ts +197 -0
  28. package/dist/WorkflowSpecSync.d.ts.map +1 -0
  29. package/dist/WorkflowSpecSync.js +474 -0
  30. package/dist/WorkflowSpecSync.js.map +1 -0
  31. package/dist/index.d.ts +19 -0
  32. package/dist/index.d.ts.map +1 -0
  33. package/dist/index.js +19 -0
  34. package/dist/index.js.map +1 -0
  35. package/dist/operations/TaskGraphOperations.d.ts +39 -0
  36. package/dist/operations/TaskGraphOperations.d.ts.map +1 -0
  37. package/dist/operations/TaskGraphOperations.js +168 -0
  38. package/dist/operations/TaskGraphOperations.js.map +1 -0
  39. package/dist/operations/WorkflowDraftOperation.d.ts +37 -0
  40. package/dist/operations/WorkflowDraftOperation.d.ts.map +1 -0
  41. package/dist/operations/WorkflowDraftOperation.js +141 -0
  42. package/dist/operations/WorkflowDraftOperation.js.map +1 -0
  43. package/dist/operations/WorkflowOperations.d.ts +22 -0
  44. package/dist/operations/WorkflowOperations.d.ts.map +1 -0
  45. package/dist/operations/WorkflowOperations.js +99 -0
  46. package/dist/operations/WorkflowOperations.js.map +1 -0
  47. package/dist/types.d.ts +328 -0
  48. package/dist/types.d.ts.map +1 -0
  49. package/dist/types.js +9 -0
  50. package/dist/types.js.map +1 -0
  51. package/package.json +36 -8
@@ -0,0 +1,197 @@
1
+ /**
2
+ * @fileoverview Persists a `WorkflowSpec` by **reconciling substrates that already exist**.
3
+ *
4
+ * **The central design constraint: no new storage.** There is no `Workflow` table and there will not
5
+ * be one. A workflow's WHAT is a Flow agent (the graph); its WHEN is a Scheduled Job or an Entity
6
+ * Action binding. Inventing a parallel `Workflow` row would create a second definition of "a
7
+ * scheduled thing", and the scheduler would then have two masters that can disagree — which is
8
+ * exactly the class of divergence this whole program has been removing.
9
+ *
10
+ * So this is a **reconciler**, not a writer. It follows the pattern
11
+ * `MJRecordProcessEntityServer.Save()` already proved: resolve the job type, find the rows this
12
+ * definition owns, then upsert or disable them so the substrate matches the spec. Rows are matched
13
+ * by a marker in their own `Configuration`, which is what makes ownership survive a rename.
14
+ *
15
+ * **Agent persistence crosses a seam.** Writing the Flow agent behind the graph is `AgentSpecSync`'s
16
+ * job — it already owns atomic multi-entity agent writes and the mutation audit. Importing it here
17
+ * would pull the agent-manager package into the execution substrate, so the host injects it instead.
18
+ * A caller with no agent writer still gets correct trigger reconciliation and an honest error rather
19
+ * than a half-persisted workflow.
20
+ *
21
+ * @module @memberjunction/task-graph
22
+ */
23
+ import { IMetadataProvider, UserInfo } from '@memberjunction/core';
24
+ import { type WorkflowSpec } from '@memberjunction/ai-core-plus';
25
+ /**
26
+ * Scheduled Job Type that runs a workflow's Flow agent.
27
+ *
28
+ * `'Agent'` is an existing seeded type backed by `AgentScheduledJobDriver` — a workflow's schedule
29
+ * reuses it rather than introducing a parallel one. `ScheduledJobType.DriverClass` is UNIQUE, so a
30
+ * second type for the same driver is not merely redundant, it is impossible; discovering that is
31
+ * what confirmed the substrate was already there and only the authoring surface was missing.
32
+ */
33
+ export declare const RUN_WORKFLOW_JOB_TYPE = "Agent";
34
+ /** Marker written into an owned row's `Configuration`, so ownership survives a rename. */
35
+ export declare const WORKFLOW_OWNER_KEY = "WorkflowAgentID";
36
+ /**
37
+ * The Action an entity-change trigger dispatches to.
38
+ *
39
+ * `Execute Agent` already exists and was written for exactly this: "a concrete dispatch target for
40
+ * `AIAgent.ExposeAsAction`". Entity-action *invocation* is likewise already wired — the save pipeline
41
+ * fires validate / before-save / after-save / before-delete / after-delete through
42
+ * `HandleEntityActions`. So an entity-change trigger needs no new machinery at all; it needs a
43
+ * binding row, which is what this creates.
44
+ */
45
+ export declare const EXECUTE_AGENT_ACTION = "Execute Agent";
46
+ /** Everything reconciliation needs beyond the spec itself. */
47
+ export type WorkflowSyncContext = {
48
+ ContextUser: UserInfo;
49
+ Provider: IMetadataProvider;
50
+ };
51
+ /**
52
+ * Persists the Flow agent behind a workflow's graph.
53
+ *
54
+ * Injected rather than imported so this package does not depend on the agent-manager. The host
55
+ * supplies an implementation backed by `AgentSpecSync`, which is the one place that writes an agent.
56
+ */
57
+ export type WorkflowAgentWriter = {
58
+ /** Creates or updates the Flow agent for this workflow. Returns its ID. */
59
+ PersistFlowAgent(spec: WorkflowSpec, context: WorkflowSyncContext): Promise<string>;
60
+ };
61
+ export type WorkflowSyncResult = {
62
+ Success: boolean;
63
+ /** The Flow agent the workflow's graph persisted as — the handle for everything downstream. */
64
+ AgentID?: string;
65
+ /** Scheduled Jobs created, updated or disabled by this reconciliation. */
66
+ ScheduledJobIDs: string[];
67
+ /** Triggers the spec asked for that this build cannot yet reconcile, stated rather than dropped. */
68
+ Unreconciled: string[];
69
+ ErrorMessage?: string;
70
+ };
71
+ export declare class WorkflowSpecSync {
72
+ private readonly agentWriter;
73
+ constructor(agentWriter?: WorkflowAgentWriter | null);
74
+ /**
75
+ * Reconciles every substrate a workflow owns, so they match the spec.
76
+ *
77
+ * Order matters: the agent is persisted first because a Scheduled Job needs its ID to point at,
78
+ * and a job pointing at an agent that does not exist would be a scheduled no-op — the failure
79
+ * mode where everything looks configured and nothing ever runs.
80
+ */
81
+ Persist(spec: WorkflowSpec, context: WorkflowSyncContext): Promise<WorkflowSyncResult>;
82
+ /**
83
+ * Brings the workflow's owned trigger rows in line with its spec.
84
+ *
85
+ * A trigger the spec no longer names has its owned row **disabled rather than deleted**. Deleting
86
+ * would destroy the run history attached to it, and a workflow whose schedule someone removed by
87
+ * mistake should be recoverable — the row carries counts, last-run and next-run that are the only
88
+ * record that it ever fired.
89
+ */
90
+ private reconcileTriggers;
91
+ /** Identity of a schedule within a workflow — cron plus zone, matching `TriggerKey`. */
92
+ private scheduleKey;
93
+ /** The same identity, read back off a persisted job. */
94
+ private scheduleKeyOf;
95
+ /**
96
+ * Binds an entity-change trigger by creating the Entity Action rows the save pipeline already
97
+ * reads.
98
+ *
99
+ * Nothing here teaches the platform a new trick. `HandleEntityActions` has fired entity actions
100
+ * from the save pipeline all along; what was missing was a row saying "when an Invoice is
101
+ * updated, run Execute Agent with this agent". Three rows express that: the `EntityAction`
102
+ * (which entity, which action), the `EntityActionInvocation` (which change fires it), and an
103
+ * `EntityActionParam` carrying the agent to run.
104
+ *
105
+ * Idempotent by lookup rather than by delete-and-recreate: re-saving a workflow must not detach
106
+ * and re-attach a live trigger, because a change landing in that window would be missed.
107
+ *
108
+ * **Scope reconciles onto the binding's own `ScopeEntityID`/`ScopeRecordID`.** Those columns and
109
+ * the engine's scope resolver already exist for exactly this; leaving them unset while the spec
110
+ * declared a scope produced a workflow firing on every record of the entity while its author
111
+ * believed it was watching one. `filter` reconciles the same way — onto an `ActionFilter` bound
112
+ * through `EntityActionFilter`, the row the invocation path already consults.
113
+ */
114
+ private reconcileEntityEvent;
115
+ /**
116
+ * Brings the binding's filter row in line with the trigger's predicate.
117
+ *
118
+ * The trigger's `filter` is an expression; what the platform evaluates is an `ActionFilter.Code`
119
+ * bound through `EntityActionFilter`. That indirection is what lets a workflow narrow *when* it
120
+ * fires without the workflow layer inventing an evaluator of its own — `RunSingleFilter` already
121
+ * compiles, caches and fail-closes this code, and the change context it hands the expression is
122
+ * the same one every other entity action sees.
123
+ *
124
+ * **Removing a filter disables the binding rather than deleting the row.** The `ActionFilter`
125
+ * survives with its code intact, so re-adding the predicate is recoverable and the audit trail of
126
+ * what this trigger used to be narrowed by does not evaporate on a save. A `Disabled` binding is
127
+ * genuinely inert — {@link EntityActionInvocationSingleRecord.ResolveFilters} skips it, which
128
+ * matters because filters fail closed, so a disabled-but-honored filter would not be a no-op, it
129
+ * would be a permanent block.
130
+ */
131
+ private reconcileTriggerFilter;
132
+ /**
133
+ * Writes the filter row holding the generated predicate.
134
+ *
135
+ * Reuses the row the binding already points at, so editing a workflow's filter edits one row
136
+ * rather than accumulating an orphan per save. `UserDescription` keeps the author's expression
137
+ * verbatim next to the generated code — without it, reading the row back tells you what the
138
+ * machine produced but not what the person asked for.
139
+ */
140
+ private upsertActionFilter;
141
+ /**
142
+ * Turn the spec's scope names into the IDs the binding stores.
143
+ *
144
+ * A scope that names an entity the instance does not have is an error rather than a silent
145
+ * widening: "watch this one invoice" degrading to "watch every invoice" is the failure the
146
+ * scope columns exist to prevent.
147
+ */
148
+ private resolveScope;
149
+ /**
150
+ * Finds or creates the Entity Action binding this workflow needs.
151
+ *
152
+ * **Each workflow owns its own binding row.** `Execute Agent` is one shared action, so every
153
+ * workflow watching a given entity would otherwise land on the same `(EntityID, ActionID)` pair
154
+ * — and reusing that row means rewriting its `AgentID`, which silently repoints the first
155
+ * workflow's trigger at the second workflow's agent. Workflow A keeps looking configured, keeps
156
+ * showing its trigger, and never runs again.
157
+ *
158
+ * That shape was briefly impossible: `UQ_EntityAction_ActionID_EntityID`, added by the v5.37.x
159
+ * junction sweep, permitted one binding per (entity, action). The sweep's own stated scope was
160
+ * "pure junction tables ... with no other meaningful data columns", which `EntityAction` — with
161
+ * Status, Sequence, LoggingMode, scope columns and three owned child collections — never met.
162
+ * `V202608080100__v6.1.x__Drop_EntityAction_Uniqueness` removes it, which also unblocks the
163
+ * ordinary case of one action bound to one entity at two different events with different params.
164
+ *
165
+ * Ownership is therefore matched on the agent and the scope, not on entity + action alone.
166
+ */
167
+ private upsertEntityAction;
168
+ /**
169
+ * This workflow's own binding at this scope, if it already has one.
170
+ *
171
+ * Ownership lives in the `AgentID` param rather than on the binding row, so candidates are
172
+ * narrowed in SQL by entity + action + scope and then matched on that param. Scope is part of
173
+ * the identity because narrowing a trigger to a different record is a *different* subscription,
174
+ * not an edit of the existing one — matching without it would silently re-point the old binding.
175
+ */
176
+ private findOwnedEntityAction;
177
+ /** Finds or creates the invocation row that says which change fires the action. */
178
+ private upsertInvocation;
179
+ /**
180
+ * Finds or creates one Entity Action Param binding.
181
+ *
182
+ * `value` is null for value types the platform derives at invocation time (`Entity Object Data`,
183
+ * `Entity Object`, `Entity Field`) — for those the ValueType *is* the instruction.
184
+ */
185
+ private upsertActionParam;
186
+ private resolveJobTypeID;
187
+ /**
188
+ * Finds the Scheduled Jobs this workflow owns.
189
+ *
190
+ * Matched on a marker inside `Configuration` rather than on name, so renaming a workflow does not
191
+ * orphan its schedule and leave a second one firing alongside the new row.
192
+ */
193
+ private findOwnedJobs;
194
+ /** Creates or updates one owned Scheduled Job so it matches the trigger. */
195
+ private upsertScheduledJob;
196
+ }
197
+ //# sourceMappingURL=WorkflowSpecSync.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"WorkflowSpecSync.d.ts","sourceRoot":"","sources":["../src/WorkflowSpecSync.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,OAAO,EAAE,iBAAiB,EAAgC,QAAQ,EAAE,MAAM,sBAAsB,CAAC;AAejG,OAAO,EAOH,KAAK,YAAY,EACpB,MAAM,8BAA8B,CAAC;AAEtC;;;;;;;GAOG;AACH,eAAO,MAAM,qBAAqB,UAAU,CAAC;AAE7C,0FAA0F;AAC1F,eAAO,MAAM,kBAAkB,oBAAoB,CAAC;AAEpD;;;;;;;;GAQG;AACH,eAAO,MAAM,oBAAoB,kBAAkB,CAAC;AAUpD,8DAA8D;AAC9D,MAAM,MAAM,mBAAmB,GAAG;IAC9B,WAAW,EAAE,QAAQ,CAAC;IACtB,QAAQ,EAAE,iBAAiB,CAAC;CAC/B,CAAC;AAEF;;;;;GAKG;AACH,MAAM,MAAM,mBAAmB,GAAG;IAC9B,2EAA2E;IAC3E,gBAAgB,CAAC,IAAI,EAAE,YAAY,EAAE,OAAO,EAAE,mBAAmB,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;CACvF,CAAC;AAEF,MAAM,MAAM,kBAAkB,GAAG;IAC7B,OAAO,EAAE,OAAO,CAAC;IACjB,+FAA+F;IAC/F,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,0EAA0E;IAC1E,eAAe,EAAE,MAAM,EAAE,CAAC;IAC1B,oGAAoG;IACpG,YAAY,EAAE,MAAM,EAAE,CAAC;IACvB,YAAY,CAAC,EAAE,MAAM,CAAC;CACzB,CAAC;AAEF,qBAAa,gBAAgB;IACb,OAAO,CAAC,QAAQ,CAAC,WAAW;gBAAX,WAAW,GAAE,mBAAmB,GAAG,IAAW;IAE3E;;;;;;OAMG;IACU,OAAO,CAAC,IAAI,EAAE,YAAY,EAAE,OAAO,EAAE,mBAAmB,GAAG,OAAO,CAAC,kBAAkB,CAAC;IAgCnG;;;;;;;OAOG;YACW,iBAAiB;IA+C/B,wFAAwF;IACxF,OAAO,CAAC,WAAW;IAInB,wDAAwD;IACxD,OAAO,CAAC,aAAa;IAIrB;;;;;;;;;;;;;;;;;;OAkBG;YACW,oBAAoB;IAmDlC;;;;;;;;;;;;;;;OAeG;YACW,sBAAsB;IA4CpC;;;;;;;OAOG;YACW,kBAAkB;IA4BhC;;;;;;OAMG;IACH,OAAO,CAAC,YAAY;IASpB;;;;;;;;;;;;;;;;;OAiBG;YACW,kBAAkB;IA+BhC;;;;;;;OAOG;YACW,qBAAqB;IAoCnC,mFAAmF;YACrE,gBAAgB;IAgC9B;;;;;OAKG;YACW,iBAAiB;YA4CjB,gBAAgB;IAkB9B;;;;;OAKG;YACW,aAAa;IAkB3B,4EAA4E;YAC9D,kBAAkB;CAoCnC"}