@granular-software/sdk 0.4.37 → 0.4.39

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.
@@ -0,0 +1,1080 @@
1
+ import { b0 as WSClientOptions, L as GranularQuotaProgress, T as ToolWithHandler, f as PublishToolsResult, aE as Job, g as ToolHandler, I as InstanceToolHandler, aG as ConversationMessageInput, aH as ConversationAppendResult, ar as EffectInfo, P as Prompt, aq as ToolInfo, at as EffectsChangedEvent, as as ToolsChangedEvent, D as DomainState, q as GranularOptions, br as EnvironmentImporter, u as RecordUserOptions, U as User, w as OpenEnvironmentOptions, a5 as EnvironmentData, a2 as BuildPolicy, bp as EnvironmentSetupSummary, y as ConversationSessionInfo, x as CreateSessionOptions, ba as RecordObjectOptions, bb as RecordObjectResult, bd as RecordObjectsOptions, bk as RecordImport, bg as RecordImportStatus, bl as EnvironmentRecordImportSummary, aC as EnvironmentFeedbackRecord, c as SessionHeapSnapshot, aW as SessionDocumentResult, aX as SessionCollectionListOptions, aZ as SessionCollectionListResult, aI as SessionConversationMessage, aJ as SessionTimelineEvent, aY as SessionJobListOptions, aP as SessionJobRecord, aN as SessionFileRecord, aO as SessionFileUploadOptions, S as SessionHeapEntry, b as SessionHeapList, aS as SessionHeapVariable, d as SessionTranscriptEntry, bN as GraphQLResult, aU as RecordSearchOptions, aT as RecordSearchResult, aV as RecordMentionInput, b9 as DefineRelationshipOptions, b8 as RelationshipInfo, b7 as ModelRef, bM as ManifestContent, bf as RecordImportOptions, C as ConnectOptions, bn as RunEnvironmentImporterOptions, l as OpenAIUsageSpendEvent, G as GranularSpendContext, o as RecordOpenAIUsageSpendResult, X as SandboxListResponse, V as Sandbox, W as CreateSandboxData, bP as DeleteResponse, Z as PermissionProfile, _ as CreatePermissionProfileData, a6 as CreateEnvironmentData, bQ as StreamEvent, bR as StreamSubscription, bS as StreamStats, v as Subject, a1 as AssignmentListResponse } from './spend-tAz2a16I.js';
2
+ import * as Automerge from '@automerge/automerge';
3
+ import { Doc } from '@automerge/automerge/slim';
4
+
5
+ declare class WSClient {
6
+ private ws;
7
+ private url;
8
+ private sessionId;
9
+ private token;
10
+ private messageQueue;
11
+ private syncHandlers;
12
+ private rpcHandlers;
13
+ private eventHandlers;
14
+ private nextRpcId;
15
+ doc: Automerge.Doc<Record<string, unknown>>;
16
+ private syncState;
17
+ private reconnectTimer;
18
+ private tokenRefreshTimer;
19
+ private isExplicitlyDisconnected;
20
+ private options;
21
+ constructor(options: WSClientOptions);
22
+ get currentSessionId(): string;
23
+ private clearTokenRefreshTimer;
24
+ private decodeBase64Url;
25
+ private getTokenExpiryMs;
26
+ private scheduleTokenRefresh;
27
+ private refreshTokenInBackground;
28
+ private resolveTokenForConnect;
29
+ /**
30
+ * Connect to the WebSocket server
31
+ * @returns {Promise<void>} Resolves when connection is open
32
+ */
33
+ connect(): Promise<void>;
34
+ private normalizeReason;
35
+ private rejectPending;
36
+ private buildDisconnectError;
37
+ private handleDisconnect;
38
+ private handleMessage;
39
+ /**
40
+ * Make an RPC call to the server
41
+ * @param {string} method - RPC method name
42
+ * @param {unknown} params - Request parameters
43
+ * @returns {Promise<unknown>} Response result
44
+ * @throws {Error} If connection is closed or timeout occurs
45
+ */
46
+ call(method: string, params: unknown): Promise<unknown>;
47
+ private handleIncomingRpc;
48
+ /**
49
+ * Subscribe to client events
50
+ * @param {string} event - Event name
51
+ * @param {Function} handler - Event handler
52
+ */
53
+ on(event: string, handler: (params: unknown) => void): void;
54
+ /**
55
+ * Register an RPC handler for incoming server requests
56
+ * @param {string} method - RPC method name
57
+ * @param {Function} handler - Handler function
58
+ */
59
+ registerRpcHandler(method: string, handler: (params: unknown) => Promise<unknown>): void;
60
+ /**
61
+ * Unsubscribe from client events
62
+ * @param {string} event - Event name
63
+ * @param {Function} handler - Handler to remove
64
+ */
65
+ off(event: string, handler: (params: unknown) => void): void;
66
+ /**
67
+ * Emit an event locally
68
+ * @param {string} event - Event name
69
+ * @param params - Event data
70
+ */
71
+ emit(event: string, params: unknown): void;
72
+ /**
73
+ * Disconnect the WebSocket and clear state
74
+ */
75
+ disconnect(): void;
76
+ }
77
+
78
+ declare class Session {
79
+ protected client: WSClient;
80
+ private clientId;
81
+ private initialQuota;
82
+ private jobsMap;
83
+ private pendingAgentMessagesByJobId;
84
+ private eventListeners;
85
+ private toolHandlers;
86
+ /** Tracks which tools are instance methods (className set, not static) */
87
+ private instanceTools;
88
+ private currentDomainRevision;
89
+ /** Local effect registry: name → full ToolWithHandler */
90
+ private effects;
91
+ /** Last known tools for diffing */
92
+ private lastKnownTools;
93
+ /** Last seen live prompts, keyed by prompt id, for answer normalization */
94
+ private promptCache;
95
+ /** Prompt ids locally answered before the document sync catches up. */
96
+ private hiddenPromptIds;
97
+ constructor(client: WSClient, clientId?: string, options?: {
98
+ initialQuota?: GranularQuotaProgress | null;
99
+ });
100
+ private extractDomainRevisionFromDoc;
101
+ private buildLegacyEffectContext;
102
+ private stringifyConversationValue;
103
+ get document(): Doc<Record<string, unknown>>;
104
+ get quota(): GranularQuotaProgress | null;
105
+ getQuota(): GranularQuotaProgress | null;
106
+ get sessionId(): string;
107
+ get domainRevision(): string | null;
108
+ /**
109
+ * Make a raw RPC call to the session's Durable Object.
110
+ *
111
+ * Use this when you need to call an RPC method that doesn't have a
112
+ * dedicated wrapper method on the Session/Environment class.
113
+ *
114
+ * @param method - RPC method name (e.g. 'domain.fetchPackagePart')
115
+ * @param params - Request parameters
116
+ * @returns The raw RPC response
117
+ *
118
+ * @example
119
+ * ```typescript
120
+ * const result = await env.rpc('domain.fetchPackagePart', {
121
+ * moduleSpecifier: '@sandbox/domain',
122
+ * part: 'types',
123
+ * });
124
+ * ```
125
+ */
126
+ rpc<T = unknown>(method: string, params?: Record<string, unknown>): Promise<T>;
127
+ /**
128
+ * Send client hello to establish the session
129
+ */
130
+ hello(): Promise<{
131
+ ok: boolean;
132
+ environmentId?: string;
133
+ docId?: string;
134
+ graphContainerStatus?: {
135
+ lastKeepAliveAt: number;
136
+ status: "warming" | "hot" | "unknown";
137
+ };
138
+ }>;
139
+ publishTools(tools: ToolWithHandler[], revision?: string): Promise<PublishToolsResult>;
140
+ publishEffect(effect: ToolWithHandler): Promise<PublishToolsResult>;
141
+ publishEffects(effects: ToolWithHandler[]): Promise<PublishToolsResult>;
142
+ unpublishEffect(name: string): Promise<PublishToolsResult>;
143
+ unpublishAllEffects(): Promise<PublishToolsResult>;
144
+ /**
145
+ * Submit a job to execute code in the sandbox.
146
+ *
147
+ * The code can import typed classes from `./sandbox-tools`:
148
+ * ```typescript
149
+ * import { Author, Book, global_search } from './sandbox-tools';
150
+ *
151
+ * const totalAuthors = await Author.count();
152
+ * const firstAuthorsPage = await Author.page({ page: 1, perPage: 10, saveAs: 'recent_authors' });
153
+ * const authors = firstAuthorsPage.items;
154
+ * const tolkien = await Author.get({ path: 'author_tolkien' });
155
+ * const bio = await tolkien.get_bio({ detailed: true });
156
+ * const books = await tolkien.get_books();
157
+ * for await (const author of Author.iterate({ perPage: 100, maxItems: 500 })) {
158
+ * console.log(author.id);
159
+ * }
160
+ * ```
161
+ *
162
+ * Effect calls (instance methods, static methods, global functions) trigger
163
+ * `effect.invoke` RPC back to the sandbox effect host, where the registered handlers
164
+ * execute locally and return the result to the sandbox.
165
+ */
166
+ submitJob(code: string, domainRevisionOrOptions?: string | {
167
+ domainRevision?: string;
168
+ metadata?: Record<string, unknown>;
169
+ agent?: Record<string, unknown>;
170
+ }): Promise<Job>;
171
+ /**
172
+ * Register a handler for a specific tool.
173
+ * @param isInstance - If true, handler will receive (id, params) for instance method dispatch.
174
+ */
175
+ registerToolHandler(name: string, handler: ToolHandler | InstanceToolHandler, isInstance?: boolean): void;
176
+ /**
177
+ * Respond to a prompt request from the sandbox
178
+ */
179
+ answerPrompt(promptId: string, answer: unknown): Promise<void>;
180
+ appendConversationMessage(input: ConversationMessageInput): Promise<ConversationAppendResult>;
181
+ /**
182
+ * Get the current list of available effects.
183
+ * Consolidates effect declarations and live availability for the session.
184
+ */
185
+ getEffects(): EffectInfo[];
186
+ /**
187
+ * Return the currently open prompt payloads known to this session.
188
+ *
189
+ * These come from live `prompt` / `prompt.request` websocket events and
190
+ * preserve the exact shape used by `answerPrompt(...)`.
191
+ */
192
+ getPrompts(): Prompt[];
193
+ getHiddenPromptIds(): string[];
194
+ /**
195
+ * Backwards-compatible alias for `getEffects()`.
196
+ */
197
+ getTools(): ToolInfo[];
198
+ /**
199
+ * Subscribe to effect changes (added, removed, updated).
200
+ * @param callback - Function called with change events
201
+ * @returns Unsubscribe function
202
+ */
203
+ onEffectsChanged(callback: (event: EffectsChangedEvent) => void): () => void;
204
+ /**
205
+ * Backwards-compatible alias for `onEffectsChanged()`.
206
+ */
207
+ onToolsChanged(callback: (event: ToolsChangedEvent) => void): () => void;
208
+ /**
209
+ * Get the current domain state and available tools
210
+ */
211
+ getDomain(): Promise<DomainState>;
212
+ /**
213
+ * Fetch a domain package part from the backend (no fallback).
214
+ */
215
+ private fetchDomainPart;
216
+ /**
217
+ * Get TypeScript class declarations for the current domain (for LLM/code gen).
218
+ */
219
+ getDomainTypes(): Promise<string>;
220
+ /**
221
+ * Get Markdown documentation for the current domain (human-readable).
222
+ */
223
+ getDomainDocs(): Promise<string>;
224
+ /**
225
+ * Get domain documentation for LLMs. Returns types (preferred) or fallback.
226
+ */
227
+ getDomainDocumentation(): Promise<string>;
228
+ /**
229
+ * Generate markdown documentation from the domain summary.
230
+ * Class-aware: groups tools by class with property/relationship info.
231
+ */
232
+ private generateFallbackDocs;
233
+ /**
234
+ * Close the session and disconnect from the sandbox
235
+ */
236
+ disconnect(): Promise<void>;
237
+ /**
238
+ * Subscribe to session events
239
+ */
240
+ on(event: string, handler: (data: unknown) => void): () => void;
241
+ /**
242
+ * Unsubscribe from session events
243
+ */
244
+ off(event: string, handler: (data: unknown) => void): void;
245
+ private setupToolInvokeHandler;
246
+ private setupEventHandlers;
247
+ protected emit(event: string, data: unknown): void;
248
+ /**
249
+ * Check for changes in the effect catalog and emit change events if needed.
250
+ */
251
+ private checkForToolChanges;
252
+ }
253
+
254
+ type SessionFileUploadBody = Blob | ArrayBuffer | Uint8Array | string;
255
+ type EnvironmentImporterHandler = (importer: EnvironmentImporter) => Promise<void> | void;
256
+ /**
257
+ * Environment is the sessionless handle for one resolved ontology environment.
258
+ *
259
+ * Use it to query or mutate environment data directly, or to open live runtime
260
+ * sessions through `environment.sessions.*` when you need jobs, prompts, or a
261
+ * synced Automerge document.
262
+ */
263
+ declare class Environment {
264
+ private granular;
265
+ private envData;
266
+ private _apiKey;
267
+ private _apiEndpoint;
268
+ constructor(granular: Granular, envData: EnvironmentData, apiKey: string, apiEndpoint: string);
269
+ /** The environment ID */
270
+ get environmentId(): string;
271
+ /** The sandbox ID */
272
+ get sandboxId(): string;
273
+ /** The ontology ID */
274
+ get ontologyId(): string;
275
+ /** The subject ID */
276
+ get subjectId(): string;
277
+ /** The named environment slot, such as dev or prod */
278
+ get envName(): string;
279
+ /** The named environment slot, such as dev or prod */
280
+ get environment(): string;
281
+ /** The resolved ontology version backing this environment */
282
+ get versionId(): string;
283
+ /** Internal Granular user identifier for this environment */
284
+ get granularId(): string;
285
+ /** The permission profile ID */
286
+ get permissionProfileId(): string;
287
+ /** The current build policy backing this environment */
288
+ get buildPolicy(): BuildPolicy;
289
+ /** The current update state relative to the followed tag */
290
+ get updateState(): EnvironmentData["updateState"];
291
+ /** The latest setup/import run summary for this environment, when available. */
292
+ get setup(): EnvironmentSetupSummary | null;
293
+ /** Convenience flag for whether this environment trails the current tag target */
294
+ get isOutdated(): boolean;
295
+ /** The followed tag name when this environment is tag-tracked */
296
+ get tag(): string | null;
297
+ /** The GraphQL API endpoint URL */
298
+ get apiEndpoint(): string;
299
+ /** Internal auth token used for control-plane and runtime fallback requests */
300
+ get authToken(): string;
301
+ /** Base runtime URL derived from the GraphQL endpoint */
302
+ get runtimeBaseUrl(): string;
303
+ syncEnvironmentData(envData: EnvironmentData): void;
304
+ get sessions(): {
305
+ list: (options?: {
306
+ status?: "active" | "closed" | "all";
307
+ }) => Promise<ConversationSessionInfo[]>;
308
+ create: (options?: CreateSessionOptions) => Promise<EnvironmentSession>;
309
+ connect: (sessionId: string, options?: {
310
+ clientId?: string;
311
+ }) => Promise<EnvironmentSession>;
312
+ reopen: (sessionId: string, options?: {
313
+ clientId?: string;
314
+ }) => Promise<EnvironmentSession>;
315
+ close: (sessionId: string, session?: EnvironmentSession | null) => Promise<void>;
316
+ };
317
+ get data(): {
318
+ record: (record: RecordObjectOptions) => Promise<RecordObjectResult>;
319
+ recordMany: (records: RecordObjectOptions[], options?: RecordObjectsOptions) => Promise<RecordObjectResult[]>;
320
+ import: (records: RecordObjectOptions[], options?: {
321
+ batchSize?: number;
322
+ }) => Promise<RecordImport>;
323
+ listImports: (status?: RecordImportStatus) => Promise<RecordImport[]>;
324
+ getImport: (importId: string) => Promise<RecordImport>;
325
+ getImportSummary: () => Promise<EnvironmentRecordImportSummary>;
326
+ cancelImport: (importId: string) => Promise<RecordImport>;
327
+ getAwaitingCount: () => Promise<number>;
328
+ };
329
+ get feedback(): {
330
+ list: () => Promise<EnvironmentFeedbackRecord[]>;
331
+ };
332
+ /**
333
+ * Sessionless environments do not own a live transport, so disconnecting the
334
+ * environment handle itself is a no-op. This keeps the public surface
335
+ * symmetric with `EnvironmentSession.disconnect()` and lets callers always
336
+ * clean up safely without tracking whether they currently hold an environment
337
+ * or a session.
338
+ */
339
+ disconnect(): Promise<void>;
340
+ listSessions(status?: "active" | "closed" | "all"): Promise<ConversationSessionInfo[]>;
341
+ createSession(options?: CreateSessionOptions): Promise<EnvironmentSession>;
342
+ connectSession(sessionId: string, options?: {
343
+ clientId?: string;
344
+ }): Promise<EnvironmentSession>;
345
+ reopenSession(sessionId: string, options?: {
346
+ clientId?: string;
347
+ }): Promise<EnvironmentSession>;
348
+ closeSession(sessionId: string, session?: EnvironmentSession | null): Promise<void>;
349
+ listFeedback(): Promise<EnvironmentFeedbackRecord[]>;
350
+ private getRuntimeBaseUrl;
351
+ private controlPlaneRequest;
352
+ private static normalizeGraphPathSegment;
353
+ /**
354
+ * Convert a class name + application record ID into Granular's graph path.
355
+ *
356
+ * This mirrors the record-write path normalization used by the control plane.
357
+ * Keep the original customer/system ID in `real_id`; graph paths are stable
358
+ * internal addresses, not the source of truth for business identity.
359
+ */
360
+ static toGraphPath(className: string, id: string): string;
361
+ /**
362
+ * Best-effort extraction of an ID-like suffix from a graph path.
363
+ *
364
+ * Prefer the record's `real_id` field whenever exact customer/system IDs
365
+ * matter, because graph path normalization is intentionally lossy.
366
+ */
367
+ static extractIdFromGraphPath(graphPath: string, className: string): string;
368
+ /**
369
+ * Execute a GraphQL query against the environment's graph.
370
+ *
371
+ * The query uses the Granular graph query language (based on Cypher/GraphQL).
372
+ * Authentication is handled automatically using the SDK's API key.
373
+ *
374
+ * @param query - The GraphQL query string
375
+ * @param variables - Optional variables for the query
376
+ * @returns The query result data
377
+ *
378
+ * @example
379
+ * ```typescript
380
+ * // Read the workspace
381
+ * const result = await env.graphql(
382
+ * `query { model(path: "workspace") { path label submodels { path label } } }`
383
+ * );
384
+ * console.log(result.data);
385
+ *
386
+ * // Create a model
387
+ * const created = await env.graphql(
388
+ * `mutation { at(path: "workspace") { create_submodel(subpath: "my_node", label: "My Node", prototype: "Model") { model { path label } } } }`
389
+ * );
390
+ * ```
391
+ */
392
+ graphql<T = any>(query: string, variables?: Record<string, any>): Promise<GraphQLResult<T>>;
393
+ searchRecords(query: string, options?: RecordSearchOptions): Promise<RecordSearchResult[]>;
394
+ /**
395
+ * Define a relationship between two model types.
396
+ *
397
+ * Creates both submodels (if they don't exist) and links them with
398
+ * a RelationshipDef node that encodes cardinality.
399
+ *
400
+ * @example
401
+ * ```typescript
402
+ * // Author has many Books, Book has one Author
403
+ * const rel = await env.defineRelationship({
404
+ * model: 'author',
405
+ * localSubmodel: 'books',
406
+ * localIsMany: true,
407
+ * foreignModel: 'book',
408
+ * foreignSubmodel: 'author',
409
+ * foreignIsMany: false,
410
+ * });
411
+ * console.log(rel.relationship_kind); // "one_to_many"
412
+ * ```
413
+ */
414
+ defineRelationship(options: DefineRelationshipOptions): Promise<RelationshipInfo>;
415
+ /**
416
+ * Get all relationships for a model type.
417
+ *
418
+ * @param modelPath - The model type path (e.g., "author")
419
+ * @returns Array of relationships from this model's perspective
420
+ *
421
+ * @example
422
+ * ```typescript
423
+ * const rels = await env.getRelationships('author');
424
+ * for (const rel of rels) {
425
+ * console.log(`${rel.local_submodel.path} -> ${rel.foreign_model.path} (${rel.relationship_kind})`);
426
+ * }
427
+ * ```
428
+ */
429
+ getRelationships(modelPath: string): Promise<RelationshipInfo[]>;
430
+ /**
431
+ * Attach a target model to a relationship submodel.
432
+ *
433
+ * Handles cardinality automatically:
434
+ * - "One" side: sets/replaces the reference
435
+ * - "Many" side: adds the target to the collection
436
+ *
437
+ * If the target model doesn't exist, it's created as an instance of the foreign type.
438
+ * Bidirectional sync is automatic.
439
+ *
440
+ * @param modelPath - The model instance path (e.g., "tolkien")
441
+ * @param submodelPath - The relationship submodel (e.g., "books")
442
+ * @param targetPath - The target model to attach (e.g., "lord_of_the_rings")
443
+ *
444
+ * @example
445
+ * ```typescript
446
+ * // Attach a book to an author (many side)
447
+ * await env.attach('tolkien', 'books', 'lord_of_the_rings');
448
+ * // This also automatically sets lord_of_the_rings:author -> tolkien
449
+ * ```
450
+ */
451
+ attach(modelPath: string, submodelPath: string, targetPath: string): Promise<void>;
452
+ /**
453
+ * Detach a target model from a relationship submodel.
454
+ *
455
+ * Handles bidirectional cleanup automatically.
456
+ *
457
+ * @param modelPath - The model instance path
458
+ * @param submodelPath - The relationship submodel
459
+ * @param targetPath - The target to detach (optional for "one" side; omit on "many" side to detach all)
460
+ *
461
+ * @example
462
+ * ```typescript
463
+ * // Detach a specific book
464
+ * await env.detach('tolkien', 'books', 'lord_of_the_rings');
465
+ *
466
+ * // Detach all books
467
+ * await env.detach('tolkien', 'books');
468
+ * ```
469
+ */
470
+ detach(modelPath: string, submodelPath: string, targetPath?: string): Promise<void>;
471
+ /**
472
+ * List all related models through a relationship submodel.
473
+ *
474
+ * @param modelPath - The model instance path
475
+ * @param submodelPath - The relationship submodel
476
+ * @returns Array of related model references
477
+ *
478
+ * @example
479
+ * ```typescript
480
+ * const books = await env.listRelated('tolkien', 'books');
481
+ * console.log(books); // [{ path: "lord_of_the_rings", label: "Lord of the Rings" }, ...]
482
+ * ```
483
+ */
484
+ listRelated(modelPath: string, submodelPath: string): Promise<ModelRef[]>;
485
+ /**
486
+ * Apply a manifest to the current environment's graph.
487
+ *
488
+ * Translates each manifest operation into GraphQL mutations and executes them
489
+ * in order. This is the core mechanism for creating classes, fields, and
490
+ * relationships from a declarative manifest.
491
+ *
492
+ * @param manifest - The manifest content to apply
493
+ * @returns Summary of applied operations
494
+ *
495
+ * @example
496
+ * ```typescript
497
+ * await environment.applyManifest({
498
+ * schemaVersion: 2,
499
+ * name: 'my-app',
500
+ * volumes: [{
501
+ * name: 'schema',
502
+ * scope: 'sandbox',
503
+ * operations: [
504
+ * { create: 'author', extends: 'class', has: { name: { type: 'string' } } },
505
+ * { create: 'book', extends: 'class', has: { title: { type: 'string' } } },
506
+ * { defineRelationship: {
507
+ * left: 'author', right: 'book',
508
+ * leftSubmodel: 'books', rightSubmodel: 'author',
509
+ * leftIsMany: true, rightIsMany: false,
510
+ * }},
511
+ * ],
512
+ * }],
513
+ * });
514
+ * ```
515
+ */
516
+ applyManifest(manifest: ManifestContent): Promise<{
517
+ applied: number;
518
+ errors: string[];
519
+ }>;
520
+ /**
521
+ * Resolve an alias reference like "@std/class" → "class"
522
+ * Strips the alias prefix, returning the bare model path.
523
+ */
524
+ private _resolveAlias;
525
+ private _runGraphql;
526
+ private _applyFieldMetamodels;
527
+ private _applyModelMetamodels;
528
+ private _ensureWorkspaceToolsRoot;
529
+ private _storeEffectSchemas;
530
+ private _applyEffectMetamodels;
531
+ private _ensureWorkspaceStreamsRoot;
532
+ private _applyEventStreamDeclaration;
533
+ private _applyEffectDeclaration;
534
+ /**
535
+ * Apply a single manifest operation via GraphQL
536
+ */
537
+ private _applyOperation;
538
+ /**
539
+ * Apply field definitions (has) to a model via GraphQL
540
+ */
541
+ private _applyFields;
542
+ /**
543
+ * Create or update an instance of a class in the graph.
544
+ *
545
+ * Uses `instantiate` under the hood, which has find-or-create semantics:
546
+ * if an instance with the given `id` already exists for the class it is
547
+ * returned; otherwise a new instance is created. Fields are then set
548
+ * (overwriting previous values) and relationships are attached.
549
+ *
550
+ * The graph path is derived as `{className}_{id}` to ensure uniqueness
551
+ * across classes (two objects of different classes may share the same
552
+ * real-world ID). Relationship targets are also resolved automatically
553
+ * using the foreign class from the relationship definition.
554
+ *
555
+ * @param options - The object specification
556
+ * @returns The graph path, real-world ID, and creation status
557
+ *
558
+ * @example
559
+ * ```typescript
560
+ * // Create an author with fields
561
+ * const result = await env.recordObject({
562
+ * className: 'author',
563
+ * id: 'tolkien',
564
+ * label: 'J.R.R. Tolkien',
565
+ * fields: { name: 'J.R.R. Tolkien', birth_year: 1892 },
566
+ * relationships: { books: ['lotr', 'silmarillion'] },
567
+ * });
568
+ * // result.path → 'author_tolkien' (internal graph path)
569
+ * // result.id → 'tolkien' (real-world ID)
570
+ * // result.created → true
571
+ * ```
572
+ */
573
+ recordObject(options: RecordObjectOptions): Promise<RecordObjectResult>;
574
+ /**
575
+ * Batch version of `recordObject()`.
576
+ *
577
+ * Sends rows through the control-plane **`/records/batch`** endpoint in **chunks** (default
578
+ * **100** records per HTTP request) so individual requests stay bounded and gateway timeouts are
579
+ * unlikely. Each chunk is retried on transient network / worker errors.
580
+ *
581
+ * Use the optional second argument to:
582
+ * - **`batchSize`** — rows per POST (smaller = more progress events; larger = fewer round trips).
583
+ * - **`concurrency`** — run up to N chunk POSTs in parallel (capped at 16) when you want lower wall time.
584
+ * - **`onChunkComplete`** — hook for UIs after each chunk succeeds (row order in the returned array
585
+ * always matches `records`; chunk **completion** order may differ when `concurrency > 1`).
586
+ *
587
+ * For **asynchronous** ingestion with worker-side batching and aggregate counters (`queued`,
588
+ * `completed`, …), use **`enqueueRecordImport`** and poll **`getRecordImport`** /
589
+ * **`getRecordImportSummary`** — best for very large fire-and-forget loads when immediate
590
+ * synchronous commit of every row is not required.
591
+ */
592
+ recordObjects(records: RecordObjectOptions[], options?: RecordObjectsOptions): Promise<RecordObjectResult[]>;
593
+ private executeRecordObjectsChunk;
594
+ /**
595
+ * Queue a background record import for this environment (async worker pipeline).
596
+ *
597
+ * **vs `recordObjects`:** this path accepts the full payload in one request, returns an
598
+ * **`importId`**, and processes rows in the background — use **`getRecordImport`** /
599
+ * **`getRecordImportSummary`** for progress. Choose it for large bulk loads where you do not
600
+ * need every row committed before the HTTP call returns. Use **`recordObjects`** when you need
601
+ * synchronous upserts and fine-grained chunk progress via **`onChunkComplete`**.
602
+ */
603
+ enqueueRecordImport(records: RecordObjectOptions[], options?: RecordImportOptions): Promise<RecordImport>;
604
+ /**
605
+ * List queued or completed record imports for this environment.
606
+ */
607
+ listRecordImports(status?: RecordImportStatus): Promise<RecordImport[]>;
608
+ /**
609
+ * Fetch the latest aggregate import counters for this environment.
610
+ */
611
+ getRecordImportSummary(): Promise<EnvironmentRecordImportSummary>;
612
+ /**
613
+ * Convenience helper returning queued + processing records for this environment.
614
+ */
615
+ getAwaitingRecordCount(): Promise<number>;
616
+ /**
617
+ * Fetch a single record import by id.
618
+ */
619
+ getRecordImport(importId: string): Promise<RecordImport>;
620
+ /**
621
+ * Cancel a queued/background record import.
622
+ */
623
+ cancelRecordImport(importId: string): Promise<RecordImport>;
624
+ }
625
+ /**
626
+ * Live runtime session attached to one opened environment.
627
+ *
628
+ * This is the object returned by `environment.sessions.create()` and friends.
629
+ * It owns websocket state, prompts, job execution, and the synced Automerge
630
+ * document while delegating environment-level data APIs back to
631
+ * `session.environment`.
632
+ */
633
+ declare class EnvironmentSession extends Session {
634
+ readonly environment: Environment;
635
+ private readonly sessionDataRoutePrefix;
636
+ /** The last known graph container status, updated by checkReadiness() or on heartbeat */
637
+ graphContainerStatus: {
638
+ lastKeepAliveAt: number;
639
+ status: "warming" | "hot" | "unknown";
640
+ } | null;
641
+ constructor(client: WSClient, environment: Environment, clientId: string, options?: {
642
+ sessionDataRoutePrefix?: string;
643
+ initialQuota?: GranularQuotaProgress | null;
644
+ });
645
+ get environmentId(): string;
646
+ get sandboxId(): string;
647
+ get ontologyId(): string;
648
+ get subjectId(): string;
649
+ get envName(): string;
650
+ get tag(): string | null;
651
+ get versionId(): string;
652
+ get granularId(): string;
653
+ get permissionProfileId(): string;
654
+ get apiEndpoint(): string;
655
+ get data(): {
656
+ record: (record: RecordObjectOptions) => Promise<RecordObjectResult>;
657
+ recordMany: (records: RecordObjectOptions[], options?: RecordObjectsOptions) => Promise<RecordObjectResult[]>;
658
+ import: (records: RecordObjectOptions[], options?: {
659
+ batchSize?: number;
660
+ } | undefined) => Promise<RecordImport>;
661
+ listImports: (status?: RecordImportStatus) => Promise<RecordImport[]>;
662
+ getImport: (importId: string) => Promise<RecordImport>;
663
+ getImportSummary: () => Promise<EnvironmentRecordImportSummary>;
664
+ cancelImport: (importId: string) => Promise<RecordImport>;
665
+ getAwaitingCount: () => Promise<number>;
666
+ };
667
+ get feedback(): {
668
+ list: () => Promise<EnvironmentFeedbackRecord[]>;
669
+ };
670
+ /**
671
+ * Return a plain JS copy of the synced session heap.
672
+ */
673
+ getHeap(): SessionHeapSnapshot;
674
+ private buildSessionDataUrl;
675
+ private sessionDataFetch;
676
+ private sessionDataRequest;
677
+ private collectAllSessionItems;
678
+ /**
679
+ * Fetch the live session document from the runtime DO.
680
+ *
681
+ * For history and saved artifacts, prefer the collection APIs on
682
+ * `messages`, `timeline`, `jobs`, and `heap`.
683
+ */
684
+ getDocument(): Promise<SessionDocumentResult>;
685
+ get messages(): {
686
+ list: (options?: SessionCollectionListOptions) => Promise<SessionCollectionListResult<SessionConversationMessage>>;
687
+ };
688
+ get timeline(): {
689
+ list: (options?: SessionCollectionListOptions) => Promise<SessionCollectionListResult<SessionTimelineEvent>>;
690
+ };
691
+ get jobs(): {
692
+ list: (options?: SessionJobListOptions) => Promise<SessionCollectionListResult<SessionJobRecord>>;
693
+ get: (jobId: string) => Promise<SessionJobRecord>;
694
+ };
695
+ get files(): {
696
+ list: (options?: SessionCollectionListOptions & {
697
+ status?: string | null;
698
+ source?: string | null;
699
+ }) => Promise<SessionCollectionListResult<SessionFileRecord>>;
700
+ get: (fileId: string) => Promise<SessionFileRecord>;
701
+ upload: (body: SessionFileUploadBody, options?: SessionFileUploadOptions) => Promise<SessionFileRecord>;
702
+ download: (fileId: string) => Promise<ArrayBuffer>;
703
+ delete: (fileId: string) => Promise<{
704
+ deleted: boolean;
705
+ fileId: string;
706
+ }>;
707
+ };
708
+ get heap(): {
709
+ entries: {
710
+ list: (options?: SessionCollectionListOptions) => Promise<SessionCollectionListResult<SessionHeapEntry>>;
711
+ get: (path: string) => Promise<SessionHeapEntry>;
712
+ };
713
+ lists: {
714
+ list: (options?: SessionCollectionListOptions) => Promise<SessionCollectionListResult<SessionHeapList>>;
715
+ get: (name: string) => Promise<SessionHeapList>;
716
+ };
717
+ variables: {
718
+ list: (options?: SessionCollectionListOptions) => Promise<SessionCollectionListResult<SessionHeapVariable>>;
719
+ get: (name: string) => Promise<SessionHeapVariable>;
720
+ delete: (name: string) => Promise<{
721
+ deleted: boolean;
722
+ name: string;
723
+ }>;
724
+ };
725
+ };
726
+ get transcript(): {
727
+ list: (options?: SessionCollectionListOptions) => Promise<SessionCollectionListResult<SessionTranscriptEntry>>;
728
+ };
729
+ graphql<T = any>(query: string, variables?: Record<string, any>): Promise<GraphQLResult<T>>;
730
+ searchRecords(query: string, options?: RecordSearchOptions): Promise<RecordSearchResult[]>;
731
+ mentionRecord(input: RecordMentionInput): Promise<SessionHeapEntry>;
732
+ defineRelationship(options: DefineRelationshipOptions): Promise<RelationshipInfo>;
733
+ getRelationships(modelPath: string): Promise<RelationshipInfo[]>;
734
+ attach(modelPath: string, submodelPath: string, targetPath: string): Promise<void>;
735
+ detach(modelPath: string, submodelPath: string, targetPath?: string): Promise<void>;
736
+ listRelated(modelPath: string, submodelPath: string): Promise<ModelRef[]>;
737
+ applyManifest(manifest: ManifestContent): Promise<{
738
+ applied: number;
739
+ errors: string[];
740
+ }>;
741
+ recordObject(options: RecordObjectOptions): Promise<RecordObjectResult>;
742
+ recordObjects(records: RecordObjectOptions[], options?: RecordObjectsOptions): Promise<RecordObjectResult[]>;
743
+ enqueueRecordImport(records: RecordObjectOptions[], options?: RecordImportOptions): Promise<RecordImport>;
744
+ listRecordImports(status?: RecordImportStatus): Promise<RecordImport[]>;
745
+ getRecordImportSummary(): Promise<EnvironmentRecordImportSummary>;
746
+ getAwaitingRecordCount(): Promise<number>;
747
+ getRecordImport(importId: string): Promise<RecordImport>;
748
+ cancelRecordImport(importId: string): Promise<RecordImport>;
749
+ listFeedback(): Promise<EnvironmentFeedbackRecord[]>;
750
+ /**
751
+ * Close the session and disconnect from the sandbox.
752
+ *
753
+ * Sends `client.goodbye` over WebSocket first, then issues an HTTP fallback
754
+ * to the runtime goodbye endpoint if no definitive WS-side runtime notify
755
+ * acknowledgement was observed.
756
+ */
757
+ disconnect(): Promise<void>;
758
+ /**
759
+ * Close only the socket transport without sending `client.goodbye`.
760
+ */
761
+ disconnectTransport(): void;
762
+ /**
763
+ * Backwards-compatible alias for `disconnect()`.
764
+ */
765
+ close(): Promise<void>;
766
+ /**
767
+ * Check if the graph container is ready and warm.
768
+ */
769
+ checkReadiness(): Promise<{
770
+ lastKeepAliveAt: number;
771
+ status: "warming" | "hot" | "unknown";
772
+ }>;
773
+ }
774
+ declare class OntologyHandle {
775
+ private granular;
776
+ private ontologyNameOrId;
777
+ constructor(granular: Granular, ontologyNameOrId: string);
778
+ get effects(): {
779
+ register: (effect: ToolWithHandler) => Promise<void>;
780
+ registerMany: (effects: ToolWithHandler[]) => Promise<void>;
781
+ unregister: (name: string) => Promise<void>;
782
+ clear: () => Promise<void>;
783
+ disconnect: () => Promise<void>;
784
+ };
785
+ get importer(): {
786
+ onEnvironmentCreate: (handler: EnvironmentImporterHandler) => void;
787
+ clear: () => void;
788
+ };
789
+ }
790
+ declare class Granular {
791
+ private apiKey;
792
+ private apiUrl;
793
+ private httpUrl;
794
+ private tokenProvider?;
795
+ private WebSocketCtor?;
796
+ private onUnexpectedClose?;
797
+ private onReconnectError?;
798
+ private effectHostUrl?;
799
+ private debugHttp;
800
+ /** Sandbox-level effect registry: sandboxId → (effectKey@selector → ToolWithHandler) */
801
+ private sandboxEffects;
802
+ /** Live sandbox-scoped effect hosts keyed by sandboxId */
803
+ private sandboxEffectHosts;
804
+ /** In-flight host connection promises to avoid duplicate concurrent connects */
805
+ private sandboxEffectHostPromises;
806
+ /** Ontology-bound environment importer hooks keyed by the caller's ontology identifier. */
807
+ private ontologyImporters;
808
+ /** Resolved importer hooks keyed by sandboxId for fast lookups during openEnvironment(). */
809
+ private sandboxImporters;
810
+ /**
811
+ * Create a new Granular client
812
+ * @param options - Client configuration
813
+ */
814
+ constructor(options: GranularOptions);
815
+ /**
816
+ * Return an ontology-scoped handle for effects and other ontology-level APIs.
817
+ */
818
+ ontology(ontologyNameOrId: string): OntologyHandle;
819
+ registerEnvironmentImporter(ontologyNameOrId: string, handler: EnvironmentImporterHandler): void;
820
+ clearEnvironmentImporter(ontologyNameOrId: string): void;
821
+ /**
822
+ * Records/upserts a user and prepares them for sandbox connections
823
+ *
824
+ * @param options - User options
825
+ * @returns The recorded user with both `userId` and `granularId`
826
+ *
827
+ * @example
828
+ * ```typescript
829
+ * const user = await granular.recordUser({
830
+ * userId: 'user_123',
831
+ * name: 'John Doe',
832
+ * permissions: ['agent'],
833
+ * });
834
+ * ```
835
+ */
836
+ recordUser(options: RecordUserOptions): Promise<User>;
837
+ /**
838
+ * Alias for `recordUser()` with user-facing naming that matches upsert semantics.
839
+ */
840
+ upsertUser(options: RecordUserOptions): Promise<User>;
841
+ private resolveConnectUser;
842
+ /**
843
+ * Open or resolve an ontology environment for one user without opening a session.
844
+ *
845
+ * @example
846
+ * ```typescript
847
+ * const environment = await granular.openEnvironment({
848
+ * ontology: 'my-ontology',
849
+ * tag: 'dev',
850
+ * userId: 'user_123',
851
+ * permissions: ['agent'],
852
+ * });
853
+ *
854
+ * await environment.data.record({
855
+ * className: 'customer',
856
+ * id: 'acme',
857
+ * fields: { name: 'Acme' },
858
+ * });
859
+ *
860
+ * const session = await environment.sessions.create();
861
+ * const job = await session.submitJob(`return "hello";`);
862
+ * console.log(await job.result);
863
+ * ```
864
+ */
865
+ openEnvironment(options: OpenEnvironmentOptions): Promise<Environment>;
866
+ /**
867
+ * Deprecated compatibility alias for `openEnvironment()`.
868
+ *
869
+ * `connect()` no longer opens a runtime session automatically.
870
+ */
871
+ connect(options: ConnectOptions): Promise<Environment>;
872
+ /**
873
+ * Run a registered environment importer against an environment that was
874
+ * opened outside this SDK instance, for example by a delegated browser flow.
875
+ *
876
+ * This uses the same setup-run and queued record-import plumbing as
877
+ * `openEnvironment()`: importer stages, expected object counts, and queued
878
+ * import counters remain visible through `environment.setup` and
879
+ * `getRecordImportSummary()`.
880
+ */
881
+ runEnvironmentImporterForEnvironment(environmentId: string, options?: RunEnvironmentImporterOptions): Promise<EnvironmentSetupSummary | null>;
882
+ private resolveRequestedTag;
883
+ private buildManagedEnvironmentName;
884
+ private matchesTagTrackedEnvironment;
885
+ private sortEnvironmentsByRecency;
886
+ private resolveOpenEnvironmentData;
887
+ /**
888
+ * List active (open) sessions for an environment — each session is one agent conversation thread.
889
+ */
890
+ listOpenSessions(filters: {
891
+ environmentId: string;
892
+ }): Promise<ConversationSessionInfo[]>;
893
+ /**
894
+ * List closed sessions for an environment (conversations that have disconnected).
895
+ */
896
+ listClosedSessions(filters: {
897
+ environmentId: string;
898
+ }): Promise<ConversationSessionInfo[]>;
899
+ private listSessionsForEnvironment;
900
+ private normalizeConversationSession;
901
+ private static coerceIsoDate;
902
+ /**
903
+ * Create a new session (conversation) for an existing environment and connect to it.
904
+ * The runtime graph is shared across all sessions for the same environment.
905
+ */
906
+ createSession(options: {
907
+ environmentId: string;
908
+ clientId?: string;
909
+ initialHeap?: CreateSessionOptions["initialHeap"];
910
+ }): Promise<EnvironmentSession>;
911
+ /**
912
+ * Connect to an existing open session (same conversation thread) using a freshly minted WebSocket token.
913
+ */
914
+ connectSession(options: {
915
+ sessionId: string;
916
+ clientId?: string;
917
+ }): Promise<EnvironmentSession>;
918
+ recordOpenAIUsageSpend(usage: OpenAIUsageSpendEvent, context?: GranularSpendContext | null, options?: {
919
+ metadata?: Record<string, unknown> | null;
920
+ }): Promise<RecordOpenAIUsageSpendResult>;
921
+ /**
922
+ * Mark a session closed in the control plane. If `environment` is the connected handle for that
923
+ * `sessionId`, disconnects the WebSocket so the runtime tears down cleanly.
924
+ */
925
+ closeSession(sessionId: string, environment?: EnvironmentSession | null): Promise<void>;
926
+ /**
927
+ * Re-open a closed session in the index and connect to its existing runtime document.
928
+ */
929
+ reopenSession(sessionId: string, options?: {
930
+ clientId?: string;
931
+ }): Promise<EnvironmentSession>;
932
+ private resolveEnvironmentImporter;
933
+ private maybeRunEnvironmentImporter;
934
+ private runEnvironmentImporter;
935
+ private bindEnvironmentHandle;
936
+ private bindWebSocketEnvironmentSession;
937
+ private activateEnvironment;
938
+ private getSandboxEffectMap;
939
+ private serializeEffect;
940
+ private publishSandboxEffectCatalog;
941
+ private syncSandboxEffectCatalog;
942
+ private recoverEffectHost;
943
+ private startEffectHostHeartbeat;
944
+ private stopEffectHostHeartbeat;
945
+ private synchronizeEffectHost;
946
+ private ensureSandboxEffectHost;
947
+ private disconnectSandboxEffectHost;
948
+ /**
949
+ * Register an effect (tool) for a specific sandbox.
950
+ *
951
+ * @param sandboxNameOrId - The name or ID of the sandbox
952
+ * @param effect - The tool definition and handler
953
+ */
954
+ registerEffect(sandboxNameOrId: string, effect: ToolWithHandler): Promise<void>;
955
+ /**
956
+ * Register multiple effects (tools) for a specific sandbox.
957
+ *
958
+ * batch version of `registerEffect`.
959
+ */
960
+ registerEffects(sandboxNameOrId: string, effects: ToolWithHandler[]): Promise<void>;
961
+ /**
962
+ * Unregister an effect from a sandbox.
963
+ *
964
+ * Removes it from the local sandbox registry and updates the
965
+ * sandbox-scoped live catalog.
966
+ */
967
+ unregisterEffect(sandboxNameOrId: string, name: string): Promise<void>;
968
+ /**
969
+ * Disconnect one sandbox-scoped effect host, or all of them when no sandbox is provided.
970
+ *
971
+ * This is primarily useful for long-lived helper processes such as generated
972
+ * `granular-effects.ts` scripts that need to shut down cleanly on SIGINT/SIGTERM.
973
+ */
974
+ disconnectEffects(sandboxNameOrId?: string): Promise<void>;
975
+ /**
976
+ * Unregister all effects for a sandbox.
977
+ */
978
+ unregisterAllEffects(sandboxNameOrId: string): Promise<void>;
979
+ /**
980
+ * Find a sandbox by name or create it if it doesn't exist
981
+ */
982
+ private findOrCreateSandbox;
983
+ /**
984
+ * Ensure a permission profile exists for a sandbox, creating it if needed.
985
+ * If profileName matches an existing profile name, returns its ID.
986
+ * Otherwise, creates a v1 source-profile file shape with an allow default.
987
+ */
988
+ private ensurePermissionProfile;
989
+ /**
990
+ * Ensure an assignment exists for a subject in a sandbox with a permission profile
991
+ */
992
+ private ensureAssignment;
993
+ /**
994
+ * Sandbox management API
995
+ */
996
+ get sandboxes(): {
997
+ list: () => Promise<SandboxListResponse>;
998
+ get: (id: string) => Promise<Sandbox>;
999
+ create: (data: CreateSandboxData) => Promise<Sandbox>;
1000
+ update: (id: string, data: Partial<CreateSandboxData>) => Promise<Sandbox>;
1001
+ delete: (id: string) => Promise<DeleteResponse>;
1002
+ };
1003
+ /**
1004
+ * Permission Profile management for sandboxes
1005
+ */
1006
+ get permissionProfiles(): {
1007
+ list: (sandboxId: string) => Promise<PermissionProfile[]>;
1008
+ get: (sandboxId: string, profileId: string) => Promise<PermissionProfile>;
1009
+ create: (sandboxId: string, data: CreatePermissionProfileData) => Promise<PermissionProfile>;
1010
+ delete: (_sandboxId: string, _profileId: string) => Promise<DeleteResponse>;
1011
+ };
1012
+ /**
1013
+ * Environment management
1014
+ */
1015
+ get environments(): {
1016
+ list: (sandboxId: string) => Promise<EnvironmentData[]>;
1017
+ get: (environmentId: string) => Promise<EnvironmentData>;
1018
+ create: (sandboxId: string, data: CreateEnvironmentData) => Promise<EnvironmentData>;
1019
+ delete: (environmentId: string) => Promise<DeleteResponse>;
1020
+ };
1021
+ /**
1022
+ * Event stream operations: query, subscribe, and acknowledge stream events
1023
+ */
1024
+ get streams(): {
1025
+ getEvents: (params: {
1026
+ ontology: string;
1027
+ stream: string;
1028
+ environment?: string;
1029
+ session?: string;
1030
+ eventTypes?: string[];
1031
+ since?: Date;
1032
+ until?: Date;
1033
+ isAcked?: boolean;
1034
+ limit?: number;
1035
+ offset?: number;
1036
+ }) => Promise<StreamEvent[]>;
1037
+ subscribe: (params: {
1038
+ ontology: string;
1039
+ stream: string;
1040
+ environment?: string;
1041
+ session?: string;
1042
+ eventTypes?: string[];
1043
+ since?: Date;
1044
+ onEvent: (event: StreamEvent) => void;
1045
+ onError?: (err: Error) => void;
1046
+ pollIntervalMs?: number;
1047
+ }) => StreamSubscription;
1048
+ ack: (eventId: string) => Promise<void>;
1049
+ ackBatch: (eventIds: string[]) => Promise<void>;
1050
+ getStats: (params: {
1051
+ ontology: string;
1052
+ environment?: string;
1053
+ }) => Promise<StreamStats[]>;
1054
+ };
1055
+ /**
1056
+ * Subject management
1057
+ */
1058
+ get subjects(): {
1059
+ get: (subjectId: string) => Promise<Subject>;
1060
+ listAssignments: (subjectId: string) => Promise<AssignmentListResponse>;
1061
+ };
1062
+ /**
1063
+ * @deprecated Use recordUser() instead
1064
+ */
1065
+ get users(): {
1066
+ create: (data: {
1067
+ id: string;
1068
+ name?: string;
1069
+ email?: string;
1070
+ }) => Promise<Subject>;
1071
+ get: (id: string) => Promise<Subject>;
1072
+ };
1073
+ private _resolveSandboxId;
1074
+ /**
1075
+ * Make an authenticated API request
1076
+ */
1077
+ private request;
1078
+ }
1079
+
1080
+ export { Environment as E, Granular as G, OntologyHandle as O, Session as S, WSClient as W, EnvironmentSession as a };