@memberjunction/server 6.1.0-edge.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/dist/generated/generated.d.ts +163 -642
- package/dist/generated/generated.d.ts.map +1 -1
- package/dist/generated/generated.js +894 -3752
- package/dist/generated/generated.js.map +1 -1
- package/dist/index.d.ts +5 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +16 -2
- package/dist/index.js.map +1 -1
- package/dist/resolvers/TaskGraphFrameResolver.d.ts +85 -0
- package/dist/resolvers/TaskGraphFrameResolver.d.ts.map +1 -0
- package/dist/resolvers/TaskGraphFrameResolver.js +163 -0
- package/dist/resolvers/TaskGraphFrameResolver.js.map +1 -0
- package/dist/rest/RESTEndpointHandler.d.ts +0 -4
- package/dist/rest/RESTEndpointHandler.d.ts.map +1 -1
- package/dist/rest/RESTEndpointHandler.js +1 -15
- package/dist/rest/RESTEndpointHandler.js.map +1 -1
- package/dist/services/StartTaskGraphDispatcher.d.ts +33 -0
- package/dist/services/StartTaskGraphDispatcher.d.ts.map +1 -0
- package/dist/services/StartTaskGraphDispatcher.js +31 -0
- package/dist/services/StartTaskGraphDispatcher.js.map +1 -0
- package/dist/services/TaskGraphAgentRunner.d.ts +15 -0
- package/dist/services/TaskGraphAgentRunner.d.ts.map +1 -0
- package/dist/services/TaskGraphAgentRunner.js +74 -0
- package/dist/services/TaskGraphAgentRunner.js.map +1 -0
- package/dist/services/TaskGraphContinuationDeliverer.d.ts +70 -0
- package/dist/services/TaskGraphContinuationDeliverer.d.ts.map +1 -0
- package/dist/services/TaskGraphContinuationDeliverer.js +162 -0
- package/dist/services/TaskGraphContinuationDeliverer.js.map +1 -0
- package/dist/services/TaskGraphProviderFactory.d.ts +39 -0
- package/dist/services/TaskGraphProviderFactory.d.ts.map +1 -0
- package/dist/services/TaskGraphProviderFactory.js +28 -0
- package/dist/services/TaskGraphProviderFactory.js.map +1 -0
- package/package.json +92 -89
- package/src/__tests__/TaskGraphContinuationDeliverer.test.ts +249 -0
- package/src/__tests__/TaskGraphFrameResolver.test.ts +136 -0
- package/src/generated/generated.ts +623 -2552
- package/src/index.ts +17 -2
- package/src/resolvers/TaskGraphFrameResolver.ts +160 -0
- package/src/rest/RESTEndpointHandler.ts +1 -15
- package/src/services/StartTaskGraphDispatcher.ts +61 -0
- package/src/services/TaskGraphAgentRunner.ts +81 -0
- package/src/services/TaskGraphContinuationDeliverer.ts +181 -0
- package/src/services/TaskGraphProviderFactory.ts +53 -0
- package/dist/resolvers/ReportResolver.d.ts +0 -24
- package/dist/resolvers/ReportResolver.d.ts.map +0 -1
- package/dist/resolvers/ReportResolver.js +0 -198
- package/dist/resolvers/ReportResolver.js.map +0 -1
- package/dist/resolvers/TaskResolver.d.ts +0 -31
- package/dist/resolvers/TaskResolver.d.ts.map +0 -1
- package/dist/resolvers/TaskResolver.js +0 -157
- package/dist/resolvers/TaskResolver.js.map +0 -1
- package/dist/services/TaskOrchestrator.d.ts +0 -130
- package/dist/services/TaskOrchestrator.d.ts.map +0 -1
- package/dist/services/TaskOrchestrator.js +0 -692
- package/dist/services/TaskOrchestrator.js.map +0 -1
- package/src/resolvers/ReportResolver.ts +0 -165
- package/src/resolvers/TaskResolver.ts +0 -148
- package/src/resolvers/__tests__/ReportResolver.test.ts +0 -279
- package/src/services/TaskOrchestrator.ts +0 -873
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview MJServer's implementation of the task-graph `ProviderFactory` seam.
|
|
3
|
+
*
|
|
4
|
+
* The durable dispatcher runs outside any HTTP request and executes tasks concurrently, so it must
|
|
5
|
+
* never share one provider — and therefore one transaction scope and one set of entity instances —
|
|
6
|
+
* across parallel work. It needs a way to mint providers on demand.
|
|
7
|
+
*
|
|
8
|
+
* Rather than have `@memberjunction/task-graph` import MJServer to get at that machinery (which
|
|
9
|
+
* would invert the dependency: MJServer depends on task-graph, not the reverse), the package
|
|
10
|
+
* declares a `ProviderFactory` interface and the host supplies an implementation. This is that
|
|
11
|
+
* implementation.
|
|
12
|
+
*
|
|
13
|
+
* It mints exactly what the per-request path already mints — a fresh provider over the **shared
|
|
14
|
+
* connection pool**, with metadata reuse — which is proven cheap at request scale and is why the
|
|
15
|
+
* plan chose this mechanism rather than inventing a dispatcher-specific one. The pool remains the
|
|
16
|
+
* real concurrency governor; the dispatcher's own `MaxConcurrentTasks` sits below it.
|
|
17
|
+
*
|
|
18
|
+
* @module @memberjunction/server
|
|
19
|
+
*/
|
|
20
|
+
import { DatabaseProviderBase, IMetadataProvider } from '@memberjunction/core';
|
|
21
|
+
import type { ProviderFactory } from '@memberjunction/task-graph';
|
|
22
|
+
import sql from 'mssql';
|
|
23
|
+
/**
|
|
24
|
+
* Creates providers for the dispatcher, one per task execution.
|
|
25
|
+
*
|
|
26
|
+
* SQL Server path only for now, matching where the dispatcher is hosted. The PostgreSQL branch of
|
|
27
|
+
* `createPerRequestProviders` is deliberately not duplicated here — when PG hosting is needed, the
|
|
28
|
+
* right move is to lift the shared branch out of `context.ts` rather than fork it a second time.
|
|
29
|
+
*/
|
|
30
|
+
export declare class TaskGraphProviderFactory implements ProviderFactory {
|
|
31
|
+
private readonly pool;
|
|
32
|
+
constructor(pool: sql.ConnectionPool);
|
|
33
|
+
CreateProvider(): Promise<IMetadataProvider>;
|
|
34
|
+
}
|
|
35
|
+
/** Convenience for hosts that already hold a pool. */
|
|
36
|
+
export declare function CreateTaskGraphProviderFactory(pool: sql.ConnectionPool): ProviderFactory;
|
|
37
|
+
/** Re-exported for hosts that need the concrete type. */
|
|
38
|
+
export type { DatabaseProviderBase };
|
|
39
|
+
//# sourceMappingURL=TaskGraphProviderFactory.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"TaskGraphProviderFactory.d.ts","sourceRoot":"","sources":["../../src/services/TaskGraphProviderFactory.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AACH,OAAO,EAAE,oBAAoB,EAAE,iBAAiB,EAAE,MAAM,sBAAsB,CAAC;AAC/E,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,4BAA4B,CAAC;AAElE,OAAO,GAAG,MAAM,OAAO,CAAC;AAGxB;;;;;;GAMG;AACH,qBAAa,wBAAyB,YAAW,eAAe;IAChD,OAAO,CAAC,QAAQ,CAAC,IAAI;gBAAJ,IAAI,EAAE,GAAG,CAAC,cAAc;IAExC,cAAc,IAAI,OAAO,CAAC,iBAAiB,CAAC;CAS5D;AAED,sDAAsD;AACtD,wBAAgB,8BAA8B,CAAC,IAAI,EAAE,GAAG,CAAC,cAAc,GAAG,eAAe,CAExF;AAED,yDAAyD;AACzD,YAAY,EAAE,oBAAoB,EAAE,CAAC"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { SQLServerDataProvider, SQLServerProviderConfigData } from '@memberjunction/sqlserver-dataprovider';
|
|
2
|
+
import { mj_core_schema } from '../config.js';
|
|
3
|
+
/**
|
|
4
|
+
* Creates providers for the dispatcher, one per task execution.
|
|
5
|
+
*
|
|
6
|
+
* SQL Server path only for now, matching where the dispatcher is hosted. The PostgreSQL branch of
|
|
7
|
+
* `createPerRequestProviders` is deliberately not duplicated here — when PG hosting is needed, the
|
|
8
|
+
* right move is to lift the shared branch out of `context.ts` rather than fork it a second time.
|
|
9
|
+
*/
|
|
10
|
+
export class TaskGraphProviderFactory {
|
|
11
|
+
constructor(pool) {
|
|
12
|
+
this.pool = pool;
|
|
13
|
+
}
|
|
14
|
+
async CreateProvider() {
|
|
15
|
+
// `loadIfNeeded = false` (the trailing false) reuses already-loaded metadata rather than
|
|
16
|
+
// re-reading it per provider — the difference between "cheap enough to do per task" and
|
|
17
|
+
// "prohibitively expensive".
|
|
18
|
+
const config = new SQLServerProviderConfigData(this.pool, mj_core_schema, 0, undefined, undefined, false);
|
|
19
|
+
const provider = new SQLServerDataProvider();
|
|
20
|
+
await provider.Config(config);
|
|
21
|
+
return provider;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
/** Convenience for hosts that already hold a pool. */
|
|
25
|
+
export function CreateTaskGraphProviderFactory(pool) {
|
|
26
|
+
return new TaskGraphProviderFactory(pool);
|
|
27
|
+
}
|
|
28
|
+
//# sourceMappingURL=TaskGraphProviderFactory.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"TaskGraphProviderFactory.js","sourceRoot":"","sources":["../../src/services/TaskGraphProviderFactory.ts"],"names":[],"mappings":"AAqBA,OAAO,EAAE,qBAAqB,EAAE,2BAA2B,EAAE,MAAM,wCAAwC,CAAC;AAE5G,OAAO,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAE9C;;;;;;GAMG;AACH,MAAM,OAAO,wBAAwB;IACjC,YAA6B,IAAwB;QAAxB,SAAI,GAAJ,IAAI,CAAoB;IAAG,CAAC;IAElD,KAAK,CAAC,cAAc;QACvB,yFAAyF;QACzF,wFAAwF;QACxF,6BAA6B;QAC7B,MAAM,MAAM,GAAG,IAAI,2BAA2B,CAAC,IAAI,CAAC,IAAI,EAAE,cAAc,EAAE,CAAC,EAAE,SAAS,EAAE,SAAS,EAAE,KAAK,CAAC,CAAC;QAC1G,MAAM,QAAQ,GAAG,IAAI,qBAAqB,EAAE,CAAC;QAC7C,MAAM,QAAQ,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;QAC9B,OAAO,QAAwC,CAAC;IACpD,CAAC;CACJ;AAED,sDAAsD;AACtD,MAAM,UAAU,8BAA8B,CAAC,IAAwB;IACnE,OAAO,IAAI,wBAAwB,CAAC,IAAI,CAAC,CAAC;AAC9C,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@memberjunction/server",
|
|
3
|
-
"version": "6.1.0-edge.
|
|
3
|
+
"version": "6.1.0-edge.1",
|
|
4
4
|
"description": "MemberJunction: This project provides API access via GraphQL to the common data store.",
|
|
5
5
|
"main": "./dist/index.js",
|
|
6
6
|
"types": "./src/index.ts",
|
|
@@ -19,94 +19,95 @@
|
|
|
19
19
|
"@as-integrations/express5": "^1.0.0",
|
|
20
20
|
"@graphql-tools/schema": "^10.0.38",
|
|
21
21
|
"@graphql-tools/utils": "^11.0.0",
|
|
22
|
-
"@memberjunction/actions": "6.1.0-edge.
|
|
23
|
-
"@memberjunction/actions-apollo": "6.1.0-edge.
|
|
24
|
-
"@memberjunction/actions-base": "6.1.0-edge.
|
|
25
|
-
"@memberjunction/actions-bizapps-accounting": "6.1.0-edge.
|
|
26
|
-
"@memberjunction/actions-bizapps-crm": "6.1.0-edge.
|
|
27
|
-
"@memberjunction/actions-bizapps-formbuilders": "6.1.0-edge.
|
|
28
|
-
"@memberjunction/actions-bizapps-lms": "6.1.0-edge.
|
|
29
|
-
"@memberjunction/actions-bizapps-social": "6.1.0-edge.
|
|
30
|
-
"@memberjunction/ai": "6.1.0-edge.
|
|
31
|
-
"@memberjunction/ai-agent-manager": "6.1.0-edge.
|
|
32
|
-
"@memberjunction/ai-agent-manager-actions": "6.1.0-edge.
|
|
33
|
-
"@memberjunction/ai-agents": "6.1.0-edge.
|
|
34
|
-
"@memberjunction/ai-bridge-base": "6.1.0-edge.
|
|
35
|
-
"@memberjunction/ai-bridge-ringcentral": "6.1.0-edge.
|
|
36
|
-
"@memberjunction/ai-bridge-server": "6.1.0-edge.
|
|
37
|
-
"@memberjunction/ai-bridge-teams": "6.1.0-edge.
|
|
38
|
-
"@memberjunction/ai-bridge-twilio": "6.1.0-edge.
|
|
39
|
-
"@memberjunction/ai-bridge-vonage": "6.1.0-edge.
|
|
40
|
-
"@memberjunction/ai-core-plus": "6.1.0-edge.
|
|
41
|
-
"@memberjunction/ai-engine-base": "6.1.0-edge.
|
|
42
|
-
"@memberjunction/ai-mcp-client": "6.1.0-edge.
|
|
43
|
-
"@memberjunction/ai-prompts": "6.1.0-edge.
|
|
44
|
-
"@memberjunction/ai-provider-bundle": "6.1.0-edge.
|
|
45
|
-
"@memberjunction/ai-vector-sync": "6.1.0-edge.
|
|
46
|
-
"@memberjunction/ai-vectordb": "6.1.0-edge.
|
|
47
|
-
"@memberjunction/ai-vectors-pinecone": "6.1.0-edge.
|
|
48
|
-
"@memberjunction/aiengine": "6.1.0-edge.
|
|
49
|
-
"@memberjunction/api-keys": "6.1.0-edge.
|
|
50
|
-
"@memberjunction/auth-providers": "6.1.0-edge.
|
|
51
|
-
"@memberjunction/clustering-engine": "6.1.0-edge.
|
|
52
|
-
"@memberjunction/codegen-lib": "6.1.0-edge.
|
|
53
|
-
"@memberjunction/communication-engine": "6.1.0-edge.
|
|
54
|
-
"@memberjunction/communication-ms-graph": "6.1.0-edge.
|
|
55
|
-
"@memberjunction/communication-sendgrid": "6.1.0-edge.
|
|
56
|
-
"@memberjunction/communication-types": "6.1.0-edge.
|
|
57
|
-
"@memberjunction/component-registry-client-sdk": "6.1.0-edge.
|
|
58
|
-
"@memberjunction/computer-use": "6.1.0-edge.
|
|
59
|
-
"@memberjunction/computer-use-engine": "6.1.0-edge.
|
|
60
|
-
"@memberjunction/config": "6.1.0-edge.
|
|
61
|
-
"@memberjunction/core": "6.1.0-edge.
|
|
62
|
-
"@memberjunction/core-actions": "6.1.0-edge.
|
|
63
|
-
"@memberjunction/core-entities": "6.1.0-edge.
|
|
64
|
-
"@memberjunction/core-entities-server": "6.1.0-edge.
|
|
65
|
-
"@memberjunction/credentials": "6.1.0-edge.
|
|
66
|
-
"@memberjunction/data-context": "6.1.0-edge.
|
|
67
|
-
"@memberjunction/data-context-server": "6.1.0-edge.
|
|
68
|
-
"@memberjunction/doc-utils": "6.1.0-edge.
|
|
69
|
-
"@memberjunction/encryption": "6.1.0-edge.
|
|
70
|
-
"@memberjunction/entity-communications-base": "6.1.0-edge.
|
|
71
|
-
"@memberjunction/entity-communications-server": "6.1.0-edge.
|
|
72
|
-
"@memberjunction/esignature": "6.1.0-edge.
|
|
73
|
-
"@memberjunction/external-change-detection": "6.1.0-edge.
|
|
74
|
-
"@memberjunction/generic-database-provider": "6.1.0-edge.
|
|
75
|
-
"@memberjunction/global": "6.1.0-edge.
|
|
76
|
-
"@memberjunction/graphql-dataprovider": "6.1.0-edge.
|
|
77
|
-
"@memberjunction/integration-engine": "6.1.0-edge.
|
|
78
|
-
"@memberjunction/integration-engine-base": "6.1.0-edge.
|
|
79
|
-
"@memberjunction/integration-progress-artifacts": "6.1.0-edge.
|
|
80
|
-
"@memberjunction/integration-schema-builder": "6.1.0-edge.
|
|
81
|
-
"@memberjunction/interactive-component-types": "6.1.0-edge.
|
|
82
|
-
"@memberjunction/lists": "6.1.0-edge.
|
|
83
|
-
"@memberjunction/lists-base": "6.1.0-edge.
|
|
84
|
-
"@memberjunction/livekit-room-server": "6.1.0-edge.
|
|
85
|
-
"@memberjunction/notifications": "6.1.0-edge.
|
|
86
|
-
"@memberjunction/postgresql-dataprovider": "6.1.0-edge.
|
|
87
|
-
"@memberjunction/queue": "6.1.0-edge.
|
|
88
|
-
"@memberjunction/record-comparison": "6.1.0-edge.
|
|
89
|
-
"@memberjunction/redis-provider": "6.1.0-edge.
|
|
90
|
-
"@memberjunction/remote-browser-base": "6.1.0-edge.
|
|
91
|
-
"@memberjunction/remote-browser-cdp": "6.1.0-edge.
|
|
92
|
-
"@memberjunction/remote-browser-selfhost": "6.1.0-edge.
|
|
93
|
-
"@memberjunction/remote-browser-server": "6.1.0-edge.
|
|
94
|
-
"@memberjunction/scheduling-actions": "6.1.0-edge.
|
|
95
|
-
"@memberjunction/scheduling-base-types": "6.1.0-edge.
|
|
96
|
-
"@memberjunction/scheduling-engine": "6.1.0-edge.
|
|
97
|
-
"@memberjunction/scheduling-engine-base": "6.1.0-edge.
|
|
98
|
-
"@memberjunction/schema-engine": "6.1.0-edge.
|
|
99
|
-
"@memberjunction/search-engine": "6.1.0-edge.
|
|
100
|
-
"@memberjunction/server-extensions-core": "6.1.0-edge.
|
|
101
|
-
"@memberjunction/sql-dialect": "6.1.0-edge.
|
|
102
|
-
"@memberjunction/sqlserver-dataprovider": "6.1.0-edge.
|
|
103
|
-
"@memberjunction/storage": "6.1.0-edge.
|
|
104
|
-
"@memberjunction/tag-engine": "6.1.0-edge.
|
|
105
|
-
"@memberjunction/tag-engine-base": "6.1.0-edge.
|
|
106
|
-
"@memberjunction/
|
|
107
|
-
"@memberjunction/
|
|
108
|
-
"@memberjunction/testing-engine
|
|
109
|
-
"@memberjunction/
|
|
22
|
+
"@memberjunction/actions": "6.1.0-edge.1",
|
|
23
|
+
"@memberjunction/actions-apollo": "6.1.0-edge.1",
|
|
24
|
+
"@memberjunction/actions-base": "6.1.0-edge.1",
|
|
25
|
+
"@memberjunction/actions-bizapps-accounting": "6.1.0-edge.1",
|
|
26
|
+
"@memberjunction/actions-bizapps-crm": "6.1.0-edge.1",
|
|
27
|
+
"@memberjunction/actions-bizapps-formbuilders": "6.1.0-edge.1",
|
|
28
|
+
"@memberjunction/actions-bizapps-lms": "6.1.0-edge.1",
|
|
29
|
+
"@memberjunction/actions-bizapps-social": "6.1.0-edge.1",
|
|
30
|
+
"@memberjunction/ai": "6.1.0-edge.1",
|
|
31
|
+
"@memberjunction/ai-agent-manager": "6.1.0-edge.1",
|
|
32
|
+
"@memberjunction/ai-agent-manager-actions": "6.1.0-edge.1",
|
|
33
|
+
"@memberjunction/ai-agents": "6.1.0-edge.1",
|
|
34
|
+
"@memberjunction/ai-bridge-base": "6.1.0-edge.1",
|
|
35
|
+
"@memberjunction/ai-bridge-ringcentral": "6.1.0-edge.1",
|
|
36
|
+
"@memberjunction/ai-bridge-server": "6.1.0-edge.1",
|
|
37
|
+
"@memberjunction/ai-bridge-teams": "6.1.0-edge.1",
|
|
38
|
+
"@memberjunction/ai-bridge-twilio": "6.1.0-edge.1",
|
|
39
|
+
"@memberjunction/ai-bridge-vonage": "6.1.0-edge.1",
|
|
40
|
+
"@memberjunction/ai-core-plus": "6.1.0-edge.1",
|
|
41
|
+
"@memberjunction/ai-engine-base": "6.1.0-edge.1",
|
|
42
|
+
"@memberjunction/ai-mcp-client": "6.1.0-edge.1",
|
|
43
|
+
"@memberjunction/ai-prompts": "6.1.0-edge.1",
|
|
44
|
+
"@memberjunction/ai-provider-bundle": "6.1.0-edge.1",
|
|
45
|
+
"@memberjunction/ai-vector-sync": "6.1.0-edge.1",
|
|
46
|
+
"@memberjunction/ai-vectordb": "6.1.0-edge.1",
|
|
47
|
+
"@memberjunction/ai-vectors-pinecone": "6.1.0-edge.1",
|
|
48
|
+
"@memberjunction/aiengine": "6.1.0-edge.1",
|
|
49
|
+
"@memberjunction/api-keys": "6.1.0-edge.1",
|
|
50
|
+
"@memberjunction/auth-providers": "6.1.0-edge.1",
|
|
51
|
+
"@memberjunction/clustering-engine": "6.1.0-edge.1",
|
|
52
|
+
"@memberjunction/codegen-lib": "6.1.0-edge.1",
|
|
53
|
+
"@memberjunction/communication-engine": "6.1.0-edge.1",
|
|
54
|
+
"@memberjunction/communication-ms-graph": "6.1.0-edge.1",
|
|
55
|
+
"@memberjunction/communication-sendgrid": "6.1.0-edge.1",
|
|
56
|
+
"@memberjunction/communication-types": "6.1.0-edge.1",
|
|
57
|
+
"@memberjunction/component-registry-client-sdk": "6.1.0-edge.1",
|
|
58
|
+
"@memberjunction/computer-use": "6.1.0-edge.1",
|
|
59
|
+
"@memberjunction/computer-use-engine": "6.1.0-edge.1",
|
|
60
|
+
"@memberjunction/config": "6.1.0-edge.1",
|
|
61
|
+
"@memberjunction/core": "6.1.0-edge.1",
|
|
62
|
+
"@memberjunction/core-actions": "6.1.0-edge.1",
|
|
63
|
+
"@memberjunction/core-entities": "6.1.0-edge.1",
|
|
64
|
+
"@memberjunction/core-entities-server": "6.1.0-edge.1",
|
|
65
|
+
"@memberjunction/credentials": "6.1.0-edge.1",
|
|
66
|
+
"@memberjunction/data-context": "6.1.0-edge.1",
|
|
67
|
+
"@memberjunction/data-context-server": "6.1.0-edge.1",
|
|
68
|
+
"@memberjunction/doc-utils": "6.1.0-edge.1",
|
|
69
|
+
"@memberjunction/encryption": "6.1.0-edge.1",
|
|
70
|
+
"@memberjunction/entity-communications-base": "6.1.0-edge.1",
|
|
71
|
+
"@memberjunction/entity-communications-server": "6.1.0-edge.1",
|
|
72
|
+
"@memberjunction/esignature": "6.1.0-edge.1",
|
|
73
|
+
"@memberjunction/external-change-detection": "6.1.0-edge.1",
|
|
74
|
+
"@memberjunction/generic-database-provider": "6.1.0-edge.1",
|
|
75
|
+
"@memberjunction/global": "6.1.0-edge.1",
|
|
76
|
+
"@memberjunction/graphql-dataprovider": "6.1.0-edge.1",
|
|
77
|
+
"@memberjunction/integration-engine": "6.1.0-edge.1",
|
|
78
|
+
"@memberjunction/integration-engine-base": "6.1.0-edge.1",
|
|
79
|
+
"@memberjunction/integration-progress-artifacts": "6.1.0-edge.1",
|
|
80
|
+
"@memberjunction/integration-schema-builder": "6.1.0-edge.1",
|
|
81
|
+
"@memberjunction/interactive-component-types": "6.1.0-edge.1",
|
|
82
|
+
"@memberjunction/lists": "6.1.0-edge.1",
|
|
83
|
+
"@memberjunction/lists-base": "6.1.0-edge.1",
|
|
84
|
+
"@memberjunction/livekit-room-server": "6.1.0-edge.1",
|
|
85
|
+
"@memberjunction/notifications": "6.1.0-edge.1",
|
|
86
|
+
"@memberjunction/postgresql-dataprovider": "6.1.0-edge.1",
|
|
87
|
+
"@memberjunction/queue": "6.1.0-edge.1",
|
|
88
|
+
"@memberjunction/record-comparison": "6.1.0-edge.1",
|
|
89
|
+
"@memberjunction/redis-provider": "6.1.0-edge.1",
|
|
90
|
+
"@memberjunction/remote-browser-base": "6.1.0-edge.1",
|
|
91
|
+
"@memberjunction/remote-browser-cdp": "6.1.0-edge.1",
|
|
92
|
+
"@memberjunction/remote-browser-selfhost": "6.1.0-edge.1",
|
|
93
|
+
"@memberjunction/remote-browser-server": "6.1.0-edge.1",
|
|
94
|
+
"@memberjunction/scheduling-actions": "6.1.0-edge.1",
|
|
95
|
+
"@memberjunction/scheduling-base-types": "6.1.0-edge.1",
|
|
96
|
+
"@memberjunction/scheduling-engine": "6.1.0-edge.1",
|
|
97
|
+
"@memberjunction/scheduling-engine-base": "6.1.0-edge.1",
|
|
98
|
+
"@memberjunction/schema-engine": "6.1.0-edge.1",
|
|
99
|
+
"@memberjunction/search-engine": "6.1.0-edge.1",
|
|
100
|
+
"@memberjunction/server-extensions-core": "6.1.0-edge.1",
|
|
101
|
+
"@memberjunction/sql-dialect": "6.1.0-edge.1",
|
|
102
|
+
"@memberjunction/sqlserver-dataprovider": "6.1.0-edge.1",
|
|
103
|
+
"@memberjunction/storage": "6.1.0-edge.1",
|
|
104
|
+
"@memberjunction/tag-engine": "6.1.0-edge.1",
|
|
105
|
+
"@memberjunction/tag-engine-base": "6.1.0-edge.1",
|
|
106
|
+
"@memberjunction/task-graph": "6.1.0-edge.1",
|
|
107
|
+
"@memberjunction/templates": "6.1.0-edge.1",
|
|
108
|
+
"@memberjunction/testing-engine": "6.1.0-edge.1",
|
|
109
|
+
"@memberjunction/testing-engine-base": "6.1.0-edge.1",
|
|
110
|
+
"@memberjunction/version-history": "6.1.0-edge.1",
|
|
110
111
|
"@octokit/auth-app": "^7.1.5",
|
|
111
112
|
"@octokit/rest": "^21.1.1",
|
|
112
113
|
"@types/compression": "^1.8.1",
|
|
@@ -148,6 +149,7 @@
|
|
|
148
149
|
"devDependencies": {
|
|
149
150
|
"@types/express": "^5.0.6",
|
|
150
151
|
"pkgroll": "2.23.0",
|
|
152
|
+
"rimraf": "6.1.2",
|
|
151
153
|
"typescript": "^5.9.3",
|
|
152
154
|
"vitest": "^3.1.1"
|
|
153
155
|
},
|
|
@@ -156,6 +158,7 @@
|
|
|
156
158
|
"url": "https://github.com/MemberJunction/MJ"
|
|
157
159
|
},
|
|
158
160
|
"scripts": {
|
|
161
|
+
"prebuild": "rimraf dist",
|
|
159
162
|
"build": "tsc && tsc-alias -f",
|
|
160
163
|
"bundle": "tsc -noEmit && pkgroll --sourcemap --minify",
|
|
161
164
|
"watch": "tsc -w",
|
|
@@ -0,0 +1,249 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tests for `TaskGraphContinuationDeliverer`.
|
|
3
|
+
*
|
|
4
|
+
* The gap this closes: the dispatcher was constructed with no deliverer at all, so a graph that
|
|
5
|
+
* finished logged its outcome, marked itself delivered, and said nothing to the conversation that
|
|
6
|
+
* asked for it — durable execution nobody hears about.
|
|
7
|
+
*
|
|
8
|
+
* The contract that matters most is that it **never throws**. The dispatcher calls this inside the
|
|
9
|
+
* compare-and-swap that marks a completion delivered; an escaping error would either abort that
|
|
10
|
+
* guard or leave the graph looking undelivered and re-notifying on every later sweep.
|
|
11
|
+
*/
|
|
12
|
+
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
|
13
|
+
|
|
14
|
+
vi.mock('@memberjunction/core', () => ({
|
|
15
|
+
LogError: vi.fn(),
|
|
16
|
+
LogStatus: vi.fn(),
|
|
17
|
+
}));
|
|
18
|
+
|
|
19
|
+
vi.mock('@memberjunction/core-entities', () => ({
|
|
20
|
+
MJConversationDetailEntity: class {},
|
|
21
|
+
MJAIAgentRunEntity: class {},
|
|
22
|
+
}));
|
|
23
|
+
|
|
24
|
+
vi.mock('@memberjunction/ai-core-plus', () => ({ MJAIAgentEntityExtended: class {} }));
|
|
25
|
+
vi.mock('@memberjunction/ai', () => ({ ChatMessageRole: { user: 'user' } }));
|
|
26
|
+
|
|
27
|
+
const runAgent = vi.fn().mockResolvedValue({ success: true });
|
|
28
|
+
vi.mock('@memberjunction/ai-agents', () => ({
|
|
29
|
+
AgentRunner: class { RunAgent = runAgent; },
|
|
30
|
+
}));
|
|
31
|
+
|
|
32
|
+
import { TaskGraphContinuationDeliverer } from '../services/TaskGraphContinuationDeliverer';
|
|
33
|
+
import type { TaskContinuationParams } from '@memberjunction/task-graph';
|
|
34
|
+
import type { UserInfo } from '@memberjunction/core';
|
|
35
|
+
|
|
36
|
+
/** A conversation-detail row that can be loaded (the source) and saved (the reply). */
|
|
37
|
+
function detailRow(over: Partial<{ loads: boolean; saves: boolean; conversationID: string }> = {}) {
|
|
38
|
+
const { loads = true, saves = true, conversationID = 'conv-1' } = over;
|
|
39
|
+
return {
|
|
40
|
+
ConversationID: conversationID,
|
|
41
|
+
Role: '', Status: '', HiddenToUser: true, Message: '',
|
|
42
|
+
NewRecord: vi.fn(),
|
|
43
|
+
Load: vi.fn().mockResolvedValue(loads),
|
|
44
|
+
Save: vi.fn().mockResolvedValue(saves),
|
|
45
|
+
LatestResult: { CompleteMessage: 'FK violation' },
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function harness(over: Parameters<typeof detailRow>[0] = {}) {
|
|
50
|
+
const rows: ReturnType<typeof detailRow>[] = [];
|
|
51
|
+
const provider = {
|
|
52
|
+
GetEntityObject: vi.fn().mockImplementation(async () => {
|
|
53
|
+
const row = detailRow(over);
|
|
54
|
+
rows.push(row);
|
|
55
|
+
return row;
|
|
56
|
+
}),
|
|
57
|
+
};
|
|
58
|
+
const providerFactory = { CreateProvider: vi.fn().mockResolvedValue(provider) };
|
|
59
|
+
const deliverer = new TaskGraphContinuationDeliverer(
|
|
60
|
+
providerFactory as never,
|
|
61
|
+
{ ID: 'user-1' } as UserInfo,
|
|
62
|
+
);
|
|
63
|
+
// First row is the source detail (Load), second is the reply (Save).
|
|
64
|
+
return { deliverer, providerFactory, provider, rows, reply: () => rows[1] };
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
const params = (over: Partial<TaskContinuationParams> = {}): TaskContinuationParams => ({
|
|
68
|
+
ParentTaskID: 'parent-1',
|
|
69
|
+
WorkflowName: 'Weekly digest',
|
|
70
|
+
ConversationDetailID: 'detail-1',
|
|
71
|
+
SubmittedByAgentRunID: 'run-1',
|
|
72
|
+
ReinvokeDepth: 0,
|
|
73
|
+
Tasks: [
|
|
74
|
+
{ TaskID: 't1', Name: 'Gather', Status: 'Complete', Summary: '412 rows' },
|
|
75
|
+
{ TaskID: 't2', Name: 'Summarize', Status: 'Complete' },
|
|
76
|
+
],
|
|
77
|
+
Summary: '2 of 2 steps completed.',
|
|
78
|
+
...over,
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
beforeEach(() => vi.clearAllMocks());
|
|
82
|
+
|
|
83
|
+
describe('posting the outcome', () => {
|
|
84
|
+
it('writes an AI-role message into the graph\'s conversation', async () => {
|
|
85
|
+
const h = harness();
|
|
86
|
+
await h.deliverer.PostMessage(params());
|
|
87
|
+
|
|
88
|
+
const reply = h.reply();
|
|
89
|
+
expect(reply.Save).toHaveBeenCalled();
|
|
90
|
+
expect(reply.ConversationID).toBe('conv-1');
|
|
91
|
+
expect(reply.Role).toBe('AI');
|
|
92
|
+
expect(reply.Status).toBe('Complete');
|
|
93
|
+
expect(reply.HiddenToUser).toBe(false);
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
it('names the workflow and carries the roll-up', async () => {
|
|
97
|
+
const h = harness();
|
|
98
|
+
await h.deliverer.PostMessage(params());
|
|
99
|
+
expect(h.reply().Message).toContain('Weekly digest');
|
|
100
|
+
expect(h.reply().Message).toContain('2 of 2 steps completed.');
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
it('lists each step, not just an aggregate status', async () => {
|
|
104
|
+
// A graph where nine of ten steps succeeded is a materially different message from one that
|
|
105
|
+
// failed outright, and the roll-up alone cannot say which step went wrong.
|
|
106
|
+
const h = harness();
|
|
107
|
+
await h.deliverer.PostMessage(params({
|
|
108
|
+
Tasks: [
|
|
109
|
+
{ TaskID: 't1', Name: 'Gather', Status: 'Complete', Summary: '412 rows' },
|
|
110
|
+
{ TaskID: 't2', Name: 'Summarize', Status: 'Failed', ErrorMessage: 'model timed out' },
|
|
111
|
+
],
|
|
112
|
+
}));
|
|
113
|
+
const message = h.reply().Message;
|
|
114
|
+
expect(message).toContain('Gather');
|
|
115
|
+
expect(message).toContain('412 rows');
|
|
116
|
+
expect(message).toContain('Summarize');
|
|
117
|
+
expect(message).toContain('model timed out');
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
it('truncates a long graph rather than burying the conversation', async () => {
|
|
121
|
+
const h = harness();
|
|
122
|
+
const many = Array.from({ length: 50 }, (_, i) => ({ TaskID: `t${i}`, Name: `Step ${i}`, Status: 'Complete' }));
|
|
123
|
+
await h.deliverer.PostMessage(params({ Tasks: many }));
|
|
124
|
+
|
|
125
|
+
const message = h.reply().Message;
|
|
126
|
+
expect(message).toContain('Step 0');
|
|
127
|
+
expect(message).not.toContain('Step 49');
|
|
128
|
+
// The count is stated rather than silently dropped — the task rows remain the full record.
|
|
129
|
+
expect(message).toContain('30 more');
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
it('mints a fresh provider per delivery', async () => {
|
|
133
|
+
// Deliveries run outside any request and concurrently with task execution; a shared provider
|
|
134
|
+
// would share one transaction scope across unrelated work.
|
|
135
|
+
const h = harness();
|
|
136
|
+
await h.deliverer.PostMessage(params());
|
|
137
|
+
expect(h.providerFactory.CreateProvider).toHaveBeenCalledTimes(1);
|
|
138
|
+
});
|
|
139
|
+
});
|
|
140
|
+
|
|
141
|
+
describe('it never throws — the dispatcher marks delivery inside a CAS guard', () => {
|
|
142
|
+
it('does nothing for a headless graph, without treating it as an error', async () => {
|
|
143
|
+
// A graph submitted by a schedule, an entity-change trigger or an API call has no
|
|
144
|
+
// conversation to answer. Most workflows are in this shape.
|
|
145
|
+
const h = harness();
|
|
146
|
+
await expect(h.deliverer.PostMessage(params({ ConversationDetailID: null }))).resolves.toBeUndefined();
|
|
147
|
+
expect(h.providerFactory.CreateProvider).not.toHaveBeenCalled();
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
it('survives a conversation detail that will not load', async () => {
|
|
151
|
+
const h = harness({ loads: false });
|
|
152
|
+
await expect(h.deliverer.PostMessage(params())).resolves.toBeUndefined();
|
|
153
|
+
expect(h.rows.length).toBe(1); // never got as far as building a reply
|
|
154
|
+
});
|
|
155
|
+
|
|
156
|
+
it('survives a failed save', async () => {
|
|
157
|
+
const h = harness({ saves: false });
|
|
158
|
+
await expect(h.deliverer.PostMessage(params())).resolves.toBeUndefined();
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
it('survives a provider that throws outright', async () => {
|
|
162
|
+
const deliverer = new TaskGraphContinuationDeliverer(
|
|
163
|
+
{ CreateProvider: vi.fn().mockRejectedValue(new Error('pool exhausted')) } as never,
|
|
164
|
+
{ ID: 'user-1' } as UserInfo,
|
|
165
|
+
);
|
|
166
|
+
await expect(deliverer.PostMessage(params())).resolves.toBeUndefined();
|
|
167
|
+
});
|
|
168
|
+
});
|
|
169
|
+
|
|
170
|
+
describe('Reinvoke — restarting the submitting agent', () => {
|
|
171
|
+
/** A provider that hands out loadable agent-run and agent rows, then the reply detail. */
|
|
172
|
+
function reinvokeHarness(over: { runLoads?: boolean; agentLoads?: boolean } = {}) {
|
|
173
|
+
const { runLoads = true, agentLoads = true } = over;
|
|
174
|
+
const rows: Array<Record<string, unknown>> = [];
|
|
175
|
+
let call = 0;
|
|
176
|
+
const provider = {
|
|
177
|
+
GetEntityObject: vi.fn().mockImplementation(async () => {
|
|
178
|
+
call++;
|
|
179
|
+
if (call === 1) {
|
|
180
|
+
const run = { AgentID: 'agent-1', Load: vi.fn().mockResolvedValue(runLoads) };
|
|
181
|
+
rows.push(run);
|
|
182
|
+
return run;
|
|
183
|
+
}
|
|
184
|
+
if (call === 2) {
|
|
185
|
+
const agent = { ID: 'agent-1', Load: vi.fn().mockResolvedValue(agentLoads) };
|
|
186
|
+
rows.push(agent);
|
|
187
|
+
return agent;
|
|
188
|
+
}
|
|
189
|
+
const detail = detailRow();
|
|
190
|
+
rows.push(detail as unknown as Record<string, unknown>);
|
|
191
|
+
return detail;
|
|
192
|
+
}),
|
|
193
|
+
};
|
|
194
|
+
const deliverer = new TaskGraphContinuationDeliverer(
|
|
195
|
+
{ CreateProvider: vi.fn().mockResolvedValue(provider) } as never,
|
|
196
|
+
{ ID: 'user-1' } as UserInfo,
|
|
197
|
+
);
|
|
198
|
+
return { deliverer, provider, rows };
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
it('starts the submitting agent a fresh turn carrying the outcome', async () => {
|
|
202
|
+
const h = reinvokeHarness();
|
|
203
|
+
await h.deliverer.Reinvoke(params());
|
|
204
|
+
|
|
205
|
+
expect(runAgent).toHaveBeenCalledTimes(1);
|
|
206
|
+
const call = runAgent.mock.calls[0][0];
|
|
207
|
+
expect(call.agent.ID).toBe('agent-1');
|
|
208
|
+
expect(call.conversationMessages[0].content).toContain('Weekly digest');
|
|
209
|
+
});
|
|
210
|
+
|
|
211
|
+
it('stamps depth + 1 — the value that makes MAX_REINVOKE_DEPTH real', async () => {
|
|
212
|
+
// Without this the next graph the restarted run submits begins the chain at zero again, and
|
|
213
|
+
// the cap can never fire. It is the whole reason ContinuationDepth exists as a column.
|
|
214
|
+
const h = reinvokeHarness();
|
|
215
|
+
await h.deliverer.Reinvoke(params({ ReinvokeDepth: 3 }));
|
|
216
|
+
expect(runAgent.mock.calls[0][0].continuationDepth).toBe(4);
|
|
217
|
+
});
|
|
218
|
+
|
|
219
|
+
it('starts a chain at 1, not 0 — depth 0 is "not a continuation"', async () => {
|
|
220
|
+
const h = reinvokeHarness();
|
|
221
|
+
await h.deliverer.Reinvoke(params({ ReinvokeDepth: 0 }));
|
|
222
|
+
expect(runAgent.mock.calls[0][0].continuationDepth).toBe(1);
|
|
223
|
+
});
|
|
224
|
+
|
|
225
|
+
it('falls back to posting when the graph records no submitting run', async () => {
|
|
226
|
+
// A schedule- or trigger-started graph has no turn to continue.
|
|
227
|
+
const h = reinvokeHarness();
|
|
228
|
+
await h.deliverer.Reinvoke(params({ SubmittedByAgentRunID: null }));
|
|
229
|
+
expect(runAgent).not.toHaveBeenCalled();
|
|
230
|
+
});
|
|
231
|
+
|
|
232
|
+
it('falls back to posting when the submitting run cannot be loaded', async () => {
|
|
233
|
+
const h = reinvokeHarness({ runLoads: false });
|
|
234
|
+
await h.deliverer.Reinvoke(params());
|
|
235
|
+
expect(runAgent).not.toHaveBeenCalled();
|
|
236
|
+
});
|
|
237
|
+
|
|
238
|
+
it('falls back to posting when the agent cannot be loaded', async () => {
|
|
239
|
+
const h = reinvokeHarness({ agentLoads: false });
|
|
240
|
+
await h.deliverer.Reinvoke(params());
|
|
241
|
+
expect(runAgent).not.toHaveBeenCalled();
|
|
242
|
+
});
|
|
243
|
+
|
|
244
|
+
it('never throws — the dispatcher marks delivery inside a CAS guard', async () => {
|
|
245
|
+
runAgent.mockRejectedValueOnce(new Error('agent exploded'));
|
|
246
|
+
const h = reinvokeHarness();
|
|
247
|
+
await expect(h.deliverer.Reinvoke(params())).resolves.toBeUndefined();
|
|
248
|
+
});
|
|
249
|
+
});
|