@granular-software/sdk 0.4.36 → 0.4.38
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/README.md +82 -2
- package/dist/agent-evals.d.mts +77 -3
- package/dist/agent-evals.d.ts +77 -3
- package/dist/agent-evals.js +2764 -643
- package/dist/agent-evals.js.map +1 -1
- package/dist/agent-evals.mjs +2763 -643
- package/dist/agent-evals.mjs.map +1 -1
- package/dist/agent-harness.d.mts +39 -4
- package/dist/agent-harness.d.ts +39 -4
- package/dist/agent-harness.js +1051 -456
- package/dist/agent-harness.js.map +1 -1
- package/dist/agent-harness.mjs +1049 -457
- package/dist/agent-harness.mjs.map +1 -1
- package/dist/cli/index.js +2470 -298
- package/dist/client-BNbWA9jQ.d.ts +1064 -0
- package/dist/client-HDJgcJC5.d.mts +1064 -0
- package/dist/index.d.mts +18 -5
- package/dist/index.d.ts +18 -5
- package/dist/index.js +2162 -575
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +2152 -576
- package/dist/index.mjs.map +1 -1
- package/dist/spend-rzS1rlFr.d.mts +1559 -0
- package/dist/spend-rzS1rlFr.d.ts +1559 -0
- package/dist/spend.d.mts +2 -0
- package/dist/spend.d.ts +2 -0
- package/dist/spend.js +111 -0
- package/dist/spend.js.map +1 -0
- package/dist/spend.mjs +107 -0
- package/dist/spend.mjs.map +1 -0
- package/package.json +8 -1
- package/dist/client-Cq8onk2D.d.mts +0 -2402
- package/dist/client-Cq8onk2D.d.ts +0 -2402
|
@@ -0,0 +1,1064 @@
|
|
|
1
|
+
import { aX 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, bm as EnvironmentImporter, u as RecordUserOptions, U as User, w as OpenEnvironmentOptions, a5 as EnvironmentData, a2 as BuildPolicy, bk as EnvironmentSetupSummary, y as ConversationSessionInfo, x as CreateSessionOptions, b5 as RecordObjectOptions, b6 as RecordObjectResult, b8 as RecordObjectsOptions, bf as RecordImport, bb as RecordImportStatus, bg as EnvironmentRecordImportSummary, aC as EnvironmentFeedbackRecord, c as SessionHeapSnapshot, aR as SessionDocumentResult, aS as SessionCollectionListOptions, aU as SessionCollectionListResult, aI as SessionConversationMessage, aJ as SessionTimelineEvent, aT as SessionJobListOptions, aK as SessionJobRecord, S as SessionHeapEntry, b as SessionHeapList, aN as SessionHeapVariable, d as SessionTranscriptEntry, bI as GraphQLResult, aP as RecordSearchOptions, aO as RecordSearchResult, aQ as RecordMentionInput, b4 as DefineRelationshipOptions, b3 as RelationshipInfo, b2 as ModelRef, bH as ManifestContent, ba as RecordImportOptions, C as ConnectOptions, bi as RunEnvironmentImporterOptions, l as OpenAIUsageSpendEvent, G as GranularSpendContext, o as RecordOpenAIUsageSpendResult, X as SandboxListResponse, V as Sandbox, W as CreateSandboxData, bK as DeleteResponse, Z as PermissionProfile, _ as CreatePermissionProfileData, a6 as CreateEnvironmentData, bL as StreamEvent, bM as StreamSubscription, bN as StreamStats, v as Subject, a1 as AssignmentListResponse } from './spend-rzS1rlFr.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 EnvironmentImporterHandler = (importer: EnvironmentImporter) => Promise<void> | void;
|
|
255
|
+
/**
|
|
256
|
+
* Environment is the sessionless handle for one resolved ontology environment.
|
|
257
|
+
*
|
|
258
|
+
* Use it to query or mutate environment data directly, or to open live runtime
|
|
259
|
+
* sessions through `environment.sessions.*` when you need jobs, prompts, or a
|
|
260
|
+
* synced Automerge document.
|
|
261
|
+
*/
|
|
262
|
+
declare class Environment {
|
|
263
|
+
private granular;
|
|
264
|
+
private envData;
|
|
265
|
+
private _apiKey;
|
|
266
|
+
private _apiEndpoint;
|
|
267
|
+
constructor(granular: Granular, envData: EnvironmentData, apiKey: string, apiEndpoint: string);
|
|
268
|
+
/** The environment ID */
|
|
269
|
+
get environmentId(): string;
|
|
270
|
+
/** The sandbox ID */
|
|
271
|
+
get sandboxId(): string;
|
|
272
|
+
/** The ontology ID */
|
|
273
|
+
get ontologyId(): string;
|
|
274
|
+
/** The subject ID */
|
|
275
|
+
get subjectId(): string;
|
|
276
|
+
/** The named environment slot, such as dev or prod */
|
|
277
|
+
get envName(): string;
|
|
278
|
+
/** The named environment slot, such as dev or prod */
|
|
279
|
+
get environment(): string;
|
|
280
|
+
/** The resolved ontology version backing this environment */
|
|
281
|
+
get versionId(): string;
|
|
282
|
+
/** Internal Granular user identifier for this environment */
|
|
283
|
+
get granularId(): string;
|
|
284
|
+
/** The permission profile ID */
|
|
285
|
+
get permissionProfileId(): string;
|
|
286
|
+
/** The current build policy backing this environment */
|
|
287
|
+
get buildPolicy(): BuildPolicy;
|
|
288
|
+
/** The current update state relative to the followed tag */
|
|
289
|
+
get updateState(): EnvironmentData["updateState"];
|
|
290
|
+
/** The latest setup/import run summary for this environment, when available. */
|
|
291
|
+
get setup(): EnvironmentSetupSummary | null;
|
|
292
|
+
/** Convenience flag for whether this environment trails the current tag target */
|
|
293
|
+
get isOutdated(): boolean;
|
|
294
|
+
/** The followed tag name when this environment is tag-tracked */
|
|
295
|
+
get tag(): string | null;
|
|
296
|
+
/** The GraphQL API endpoint URL */
|
|
297
|
+
get apiEndpoint(): string;
|
|
298
|
+
/** Internal auth token used for control-plane and runtime fallback requests */
|
|
299
|
+
get authToken(): string;
|
|
300
|
+
/** Base runtime URL derived from the GraphQL endpoint */
|
|
301
|
+
get runtimeBaseUrl(): string;
|
|
302
|
+
syncEnvironmentData(envData: EnvironmentData): void;
|
|
303
|
+
get sessions(): {
|
|
304
|
+
list: (options?: {
|
|
305
|
+
status?: "active" | "closed" | "all";
|
|
306
|
+
}) => Promise<ConversationSessionInfo[]>;
|
|
307
|
+
create: (options?: CreateSessionOptions) => Promise<EnvironmentSession>;
|
|
308
|
+
connect: (sessionId: string, options?: {
|
|
309
|
+
clientId?: string;
|
|
310
|
+
}) => Promise<EnvironmentSession>;
|
|
311
|
+
reopen: (sessionId: string, options?: {
|
|
312
|
+
clientId?: string;
|
|
313
|
+
}) => Promise<EnvironmentSession>;
|
|
314
|
+
close: (sessionId: string, session?: EnvironmentSession | null) => Promise<void>;
|
|
315
|
+
};
|
|
316
|
+
get data(): {
|
|
317
|
+
record: (record: RecordObjectOptions) => Promise<RecordObjectResult>;
|
|
318
|
+
recordMany: (records: RecordObjectOptions[], options?: RecordObjectsOptions) => Promise<RecordObjectResult[]>;
|
|
319
|
+
import: (records: RecordObjectOptions[], options?: {
|
|
320
|
+
batchSize?: number;
|
|
321
|
+
}) => Promise<RecordImport>;
|
|
322
|
+
listImports: (status?: RecordImportStatus) => Promise<RecordImport[]>;
|
|
323
|
+
getImport: (importId: string) => Promise<RecordImport>;
|
|
324
|
+
getImportSummary: () => Promise<EnvironmentRecordImportSummary>;
|
|
325
|
+
cancelImport: (importId: string) => Promise<RecordImport>;
|
|
326
|
+
getAwaitingCount: () => Promise<number>;
|
|
327
|
+
};
|
|
328
|
+
get feedback(): {
|
|
329
|
+
list: () => Promise<EnvironmentFeedbackRecord[]>;
|
|
330
|
+
};
|
|
331
|
+
/**
|
|
332
|
+
* Sessionless environments do not own a live transport, so disconnecting the
|
|
333
|
+
* environment handle itself is a no-op. This keeps the public surface
|
|
334
|
+
* symmetric with `EnvironmentSession.disconnect()` and lets callers always
|
|
335
|
+
* clean up safely without tracking whether they currently hold an environment
|
|
336
|
+
* or a session.
|
|
337
|
+
*/
|
|
338
|
+
disconnect(): Promise<void>;
|
|
339
|
+
listSessions(status?: "active" | "closed" | "all"): Promise<ConversationSessionInfo[]>;
|
|
340
|
+
createSession(options?: CreateSessionOptions): Promise<EnvironmentSession>;
|
|
341
|
+
connectSession(sessionId: string, options?: {
|
|
342
|
+
clientId?: string;
|
|
343
|
+
}): Promise<EnvironmentSession>;
|
|
344
|
+
reopenSession(sessionId: string, options?: {
|
|
345
|
+
clientId?: string;
|
|
346
|
+
}): Promise<EnvironmentSession>;
|
|
347
|
+
closeSession(sessionId: string, session?: EnvironmentSession | null): Promise<void>;
|
|
348
|
+
listFeedback(): Promise<EnvironmentFeedbackRecord[]>;
|
|
349
|
+
private getRuntimeBaseUrl;
|
|
350
|
+
private controlPlaneRequest;
|
|
351
|
+
private static normalizeGraphPathSegment;
|
|
352
|
+
/**
|
|
353
|
+
* Convert a class name + application record ID into Granular's graph path.
|
|
354
|
+
*
|
|
355
|
+
* This mirrors the record-write path normalization used by the control plane.
|
|
356
|
+
* Keep the original customer/system ID in `real_id`; graph paths are stable
|
|
357
|
+
* internal addresses, not the source of truth for business identity.
|
|
358
|
+
*/
|
|
359
|
+
static toGraphPath(className: string, id: string): string;
|
|
360
|
+
/**
|
|
361
|
+
* Best-effort extraction of an ID-like suffix from a graph path.
|
|
362
|
+
*
|
|
363
|
+
* Prefer the record's `real_id` field whenever exact customer/system IDs
|
|
364
|
+
* matter, because graph path normalization is intentionally lossy.
|
|
365
|
+
*/
|
|
366
|
+
static extractIdFromGraphPath(graphPath: string, className: string): string;
|
|
367
|
+
/**
|
|
368
|
+
* Execute a GraphQL query against the environment's graph.
|
|
369
|
+
*
|
|
370
|
+
* The query uses the Granular graph query language (based on Cypher/GraphQL).
|
|
371
|
+
* Authentication is handled automatically using the SDK's API key.
|
|
372
|
+
*
|
|
373
|
+
* @param query - The GraphQL query string
|
|
374
|
+
* @param variables - Optional variables for the query
|
|
375
|
+
* @returns The query result data
|
|
376
|
+
*
|
|
377
|
+
* @example
|
|
378
|
+
* ```typescript
|
|
379
|
+
* // Read the workspace
|
|
380
|
+
* const result = await env.graphql(
|
|
381
|
+
* `query { model(path: "workspace") { path label submodels { path label } } }`
|
|
382
|
+
* );
|
|
383
|
+
* console.log(result.data);
|
|
384
|
+
*
|
|
385
|
+
* // Create a model
|
|
386
|
+
* const created = await env.graphql(
|
|
387
|
+
* `mutation { at(path: "workspace") { create_submodel(subpath: "my_node", label: "My Node", prototype: "Model") { model { path label } } } }`
|
|
388
|
+
* );
|
|
389
|
+
* ```
|
|
390
|
+
*/
|
|
391
|
+
graphql<T = any>(query: string, variables?: Record<string, any>): Promise<GraphQLResult<T>>;
|
|
392
|
+
searchRecords(query: string, options?: RecordSearchOptions): Promise<RecordSearchResult[]>;
|
|
393
|
+
/**
|
|
394
|
+
* Define a relationship between two model types.
|
|
395
|
+
*
|
|
396
|
+
* Creates both submodels (if they don't exist) and links them with
|
|
397
|
+
* a RelationshipDef node that encodes cardinality.
|
|
398
|
+
*
|
|
399
|
+
* @example
|
|
400
|
+
* ```typescript
|
|
401
|
+
* // Author has many Books, Book has one Author
|
|
402
|
+
* const rel = await env.defineRelationship({
|
|
403
|
+
* model: 'author',
|
|
404
|
+
* localSubmodel: 'books',
|
|
405
|
+
* localIsMany: true,
|
|
406
|
+
* foreignModel: 'book',
|
|
407
|
+
* foreignSubmodel: 'author',
|
|
408
|
+
* foreignIsMany: false,
|
|
409
|
+
* });
|
|
410
|
+
* console.log(rel.relationship_kind); // "one_to_many"
|
|
411
|
+
* ```
|
|
412
|
+
*/
|
|
413
|
+
defineRelationship(options: DefineRelationshipOptions): Promise<RelationshipInfo>;
|
|
414
|
+
/**
|
|
415
|
+
* Get all relationships for a model type.
|
|
416
|
+
*
|
|
417
|
+
* @param modelPath - The model type path (e.g., "author")
|
|
418
|
+
* @returns Array of relationships from this model's perspective
|
|
419
|
+
*
|
|
420
|
+
* @example
|
|
421
|
+
* ```typescript
|
|
422
|
+
* const rels = await env.getRelationships('author');
|
|
423
|
+
* for (const rel of rels) {
|
|
424
|
+
* console.log(`${rel.local_submodel.path} -> ${rel.foreign_model.path} (${rel.relationship_kind})`);
|
|
425
|
+
* }
|
|
426
|
+
* ```
|
|
427
|
+
*/
|
|
428
|
+
getRelationships(modelPath: string): Promise<RelationshipInfo[]>;
|
|
429
|
+
/**
|
|
430
|
+
* Attach a target model to a relationship submodel.
|
|
431
|
+
*
|
|
432
|
+
* Handles cardinality automatically:
|
|
433
|
+
* - "One" side: sets/replaces the reference
|
|
434
|
+
* - "Many" side: adds the target to the collection
|
|
435
|
+
*
|
|
436
|
+
* If the target model doesn't exist, it's created as an instance of the foreign type.
|
|
437
|
+
* Bidirectional sync is automatic.
|
|
438
|
+
*
|
|
439
|
+
* @param modelPath - The model instance path (e.g., "tolkien")
|
|
440
|
+
* @param submodelPath - The relationship submodel (e.g., "books")
|
|
441
|
+
* @param targetPath - The target model to attach (e.g., "lord_of_the_rings")
|
|
442
|
+
*
|
|
443
|
+
* @example
|
|
444
|
+
* ```typescript
|
|
445
|
+
* // Attach a book to an author (many side)
|
|
446
|
+
* await env.attach('tolkien', 'books', 'lord_of_the_rings');
|
|
447
|
+
* // This also automatically sets lord_of_the_rings:author -> tolkien
|
|
448
|
+
* ```
|
|
449
|
+
*/
|
|
450
|
+
attach(modelPath: string, submodelPath: string, targetPath: string): Promise<void>;
|
|
451
|
+
/**
|
|
452
|
+
* Detach a target model from a relationship submodel.
|
|
453
|
+
*
|
|
454
|
+
* Handles bidirectional cleanup automatically.
|
|
455
|
+
*
|
|
456
|
+
* @param modelPath - The model instance path
|
|
457
|
+
* @param submodelPath - The relationship submodel
|
|
458
|
+
* @param targetPath - The target to detach (optional for "one" side; omit on "many" side to detach all)
|
|
459
|
+
*
|
|
460
|
+
* @example
|
|
461
|
+
* ```typescript
|
|
462
|
+
* // Detach a specific book
|
|
463
|
+
* await env.detach('tolkien', 'books', 'lord_of_the_rings');
|
|
464
|
+
*
|
|
465
|
+
* // Detach all books
|
|
466
|
+
* await env.detach('tolkien', 'books');
|
|
467
|
+
* ```
|
|
468
|
+
*/
|
|
469
|
+
detach(modelPath: string, submodelPath: string, targetPath?: string): Promise<void>;
|
|
470
|
+
/**
|
|
471
|
+
* List all related models through a relationship submodel.
|
|
472
|
+
*
|
|
473
|
+
* @param modelPath - The model instance path
|
|
474
|
+
* @param submodelPath - The relationship submodel
|
|
475
|
+
* @returns Array of related model references
|
|
476
|
+
*
|
|
477
|
+
* @example
|
|
478
|
+
* ```typescript
|
|
479
|
+
* const books = await env.listRelated('tolkien', 'books');
|
|
480
|
+
* console.log(books); // [{ path: "lord_of_the_rings", label: "Lord of the Rings" }, ...]
|
|
481
|
+
* ```
|
|
482
|
+
*/
|
|
483
|
+
listRelated(modelPath: string, submodelPath: string): Promise<ModelRef[]>;
|
|
484
|
+
/**
|
|
485
|
+
* Apply a manifest to the current environment's graph.
|
|
486
|
+
*
|
|
487
|
+
* Translates each manifest operation into GraphQL mutations and executes them
|
|
488
|
+
* in order. This is the core mechanism for creating classes, fields, and
|
|
489
|
+
* relationships from a declarative manifest.
|
|
490
|
+
*
|
|
491
|
+
* @param manifest - The manifest content to apply
|
|
492
|
+
* @returns Summary of applied operations
|
|
493
|
+
*
|
|
494
|
+
* @example
|
|
495
|
+
* ```typescript
|
|
496
|
+
* await environment.applyManifest({
|
|
497
|
+
* schemaVersion: 2,
|
|
498
|
+
* name: 'my-app',
|
|
499
|
+
* volumes: [{
|
|
500
|
+
* name: 'schema',
|
|
501
|
+
* scope: 'sandbox',
|
|
502
|
+
* operations: [
|
|
503
|
+
* { create: 'author', extends: 'class', has: { name: { type: 'string' } } },
|
|
504
|
+
* { create: 'book', extends: 'class', has: { title: { type: 'string' } } },
|
|
505
|
+
* { defineRelationship: {
|
|
506
|
+
* left: 'author', right: 'book',
|
|
507
|
+
* leftSubmodel: 'books', rightSubmodel: 'author',
|
|
508
|
+
* leftIsMany: true, rightIsMany: false,
|
|
509
|
+
* }},
|
|
510
|
+
* ],
|
|
511
|
+
* }],
|
|
512
|
+
* });
|
|
513
|
+
* ```
|
|
514
|
+
*/
|
|
515
|
+
applyManifest(manifest: ManifestContent): Promise<{
|
|
516
|
+
applied: number;
|
|
517
|
+
errors: string[];
|
|
518
|
+
}>;
|
|
519
|
+
/**
|
|
520
|
+
* Resolve an alias reference like "@std/class" → "class"
|
|
521
|
+
* Strips the alias prefix, returning the bare model path.
|
|
522
|
+
*/
|
|
523
|
+
private _resolveAlias;
|
|
524
|
+
private _runGraphql;
|
|
525
|
+
private _applyFieldMetamodels;
|
|
526
|
+
private _applyModelMetamodels;
|
|
527
|
+
private _ensureWorkspaceToolsRoot;
|
|
528
|
+
private _storeEffectSchemas;
|
|
529
|
+
private _applyEffectMetamodels;
|
|
530
|
+
private _ensureWorkspaceStreamsRoot;
|
|
531
|
+
private _applyEventStreamDeclaration;
|
|
532
|
+
private _applyEffectDeclaration;
|
|
533
|
+
/**
|
|
534
|
+
* Apply a single manifest operation via GraphQL
|
|
535
|
+
*/
|
|
536
|
+
private _applyOperation;
|
|
537
|
+
/**
|
|
538
|
+
* Apply field definitions (has) to a model via GraphQL
|
|
539
|
+
*/
|
|
540
|
+
private _applyFields;
|
|
541
|
+
/**
|
|
542
|
+
* Create or update an instance of a class in the graph.
|
|
543
|
+
*
|
|
544
|
+
* Uses `instantiate` under the hood, which has find-or-create semantics:
|
|
545
|
+
* if an instance with the given `id` already exists for the class it is
|
|
546
|
+
* returned; otherwise a new instance is created. Fields are then set
|
|
547
|
+
* (overwriting previous values) and relationships are attached.
|
|
548
|
+
*
|
|
549
|
+
* The graph path is derived as `{className}_{id}` to ensure uniqueness
|
|
550
|
+
* across classes (two objects of different classes may share the same
|
|
551
|
+
* real-world ID). Relationship targets are also resolved automatically
|
|
552
|
+
* using the foreign class from the relationship definition.
|
|
553
|
+
*
|
|
554
|
+
* @param options - The object specification
|
|
555
|
+
* @returns The graph path, real-world ID, and creation status
|
|
556
|
+
*
|
|
557
|
+
* @example
|
|
558
|
+
* ```typescript
|
|
559
|
+
* // Create an author with fields
|
|
560
|
+
* const result = await env.recordObject({
|
|
561
|
+
* className: 'author',
|
|
562
|
+
* id: 'tolkien',
|
|
563
|
+
* label: 'J.R.R. Tolkien',
|
|
564
|
+
* fields: { name: 'J.R.R. Tolkien', birth_year: 1892 },
|
|
565
|
+
* relationships: { books: ['lotr', 'silmarillion'] },
|
|
566
|
+
* });
|
|
567
|
+
* // result.path → 'author_tolkien' (internal graph path)
|
|
568
|
+
* // result.id → 'tolkien' (real-world ID)
|
|
569
|
+
* // result.created → true
|
|
570
|
+
* ```
|
|
571
|
+
*/
|
|
572
|
+
recordObject(options: RecordObjectOptions): Promise<RecordObjectResult>;
|
|
573
|
+
/**
|
|
574
|
+
* Batch version of `recordObject()`.
|
|
575
|
+
*
|
|
576
|
+
* Sends rows through the control-plane **`/records/batch`** endpoint in **chunks** (default
|
|
577
|
+
* **100** records per HTTP request) so individual requests stay bounded and gateway timeouts are
|
|
578
|
+
* unlikely. Each chunk is retried on transient network / worker errors.
|
|
579
|
+
*
|
|
580
|
+
* Use the optional second argument to:
|
|
581
|
+
* - **`batchSize`** — rows per POST (smaller = more progress events; larger = fewer round trips).
|
|
582
|
+
* - **`concurrency`** — run up to N chunk POSTs in parallel (capped at 16) when you want lower wall time.
|
|
583
|
+
* - **`onChunkComplete`** — hook for UIs after each chunk succeeds (row order in the returned array
|
|
584
|
+
* always matches `records`; chunk **completion** order may differ when `concurrency > 1`).
|
|
585
|
+
*
|
|
586
|
+
* For **asynchronous** ingestion with worker-side batching and aggregate counters (`queued`,
|
|
587
|
+
* `completed`, …), use **`enqueueRecordImport`** and poll **`getRecordImport`** /
|
|
588
|
+
* **`getRecordImportSummary`** — best for very large fire-and-forget loads when immediate
|
|
589
|
+
* synchronous commit of every row is not required.
|
|
590
|
+
*/
|
|
591
|
+
recordObjects(records: RecordObjectOptions[], options?: RecordObjectsOptions): Promise<RecordObjectResult[]>;
|
|
592
|
+
private executeRecordObjectsChunk;
|
|
593
|
+
/**
|
|
594
|
+
* Queue a background record import for this environment (async worker pipeline).
|
|
595
|
+
*
|
|
596
|
+
* **vs `recordObjects`:** this path accepts the full payload in one request, returns an
|
|
597
|
+
* **`importId`**, and processes rows in the background — use **`getRecordImport`** /
|
|
598
|
+
* **`getRecordImportSummary`** for progress. Choose it for large bulk loads where you do not
|
|
599
|
+
* need every row committed before the HTTP call returns. Use **`recordObjects`** when you need
|
|
600
|
+
* synchronous upserts and fine-grained chunk progress via **`onChunkComplete`**.
|
|
601
|
+
*/
|
|
602
|
+
enqueueRecordImport(records: RecordObjectOptions[], options?: RecordImportOptions): Promise<RecordImport>;
|
|
603
|
+
/**
|
|
604
|
+
* List queued or completed record imports for this environment.
|
|
605
|
+
*/
|
|
606
|
+
listRecordImports(status?: RecordImportStatus): Promise<RecordImport[]>;
|
|
607
|
+
/**
|
|
608
|
+
* Fetch the latest aggregate import counters for this environment.
|
|
609
|
+
*/
|
|
610
|
+
getRecordImportSummary(): Promise<EnvironmentRecordImportSummary>;
|
|
611
|
+
/**
|
|
612
|
+
* Convenience helper returning queued + processing records for this environment.
|
|
613
|
+
*/
|
|
614
|
+
getAwaitingRecordCount(): Promise<number>;
|
|
615
|
+
/**
|
|
616
|
+
* Fetch a single record import by id.
|
|
617
|
+
*/
|
|
618
|
+
getRecordImport(importId: string): Promise<RecordImport>;
|
|
619
|
+
/**
|
|
620
|
+
* Cancel a queued/background record import.
|
|
621
|
+
*/
|
|
622
|
+
cancelRecordImport(importId: string): Promise<RecordImport>;
|
|
623
|
+
}
|
|
624
|
+
/**
|
|
625
|
+
* Live runtime session attached to one opened environment.
|
|
626
|
+
*
|
|
627
|
+
* This is the object returned by `environment.sessions.create()` and friends.
|
|
628
|
+
* It owns websocket state, prompts, job execution, and the synced Automerge
|
|
629
|
+
* document while delegating environment-level data APIs back to
|
|
630
|
+
* `session.environment`.
|
|
631
|
+
*/
|
|
632
|
+
declare class EnvironmentSession extends Session {
|
|
633
|
+
readonly environment: Environment;
|
|
634
|
+
private readonly sessionDataRoutePrefix;
|
|
635
|
+
/** The last known graph container status, updated by checkReadiness() or on heartbeat */
|
|
636
|
+
graphContainerStatus: {
|
|
637
|
+
lastKeepAliveAt: number;
|
|
638
|
+
status: "warming" | "hot" | "unknown";
|
|
639
|
+
} | null;
|
|
640
|
+
constructor(client: WSClient, environment: Environment, clientId: string, options?: {
|
|
641
|
+
sessionDataRoutePrefix?: string;
|
|
642
|
+
initialQuota?: GranularQuotaProgress | null;
|
|
643
|
+
});
|
|
644
|
+
get environmentId(): string;
|
|
645
|
+
get sandboxId(): string;
|
|
646
|
+
get ontologyId(): string;
|
|
647
|
+
get subjectId(): string;
|
|
648
|
+
get envName(): string;
|
|
649
|
+
get tag(): string | null;
|
|
650
|
+
get versionId(): string;
|
|
651
|
+
get granularId(): string;
|
|
652
|
+
get permissionProfileId(): string;
|
|
653
|
+
get apiEndpoint(): string;
|
|
654
|
+
get data(): {
|
|
655
|
+
record: (record: RecordObjectOptions) => Promise<RecordObjectResult>;
|
|
656
|
+
recordMany: (records: RecordObjectOptions[], options?: RecordObjectsOptions) => Promise<RecordObjectResult[]>;
|
|
657
|
+
import: (records: RecordObjectOptions[], options?: {
|
|
658
|
+
batchSize?: number;
|
|
659
|
+
} | undefined) => Promise<RecordImport>;
|
|
660
|
+
listImports: (status?: RecordImportStatus) => Promise<RecordImport[]>;
|
|
661
|
+
getImport: (importId: string) => Promise<RecordImport>;
|
|
662
|
+
getImportSummary: () => Promise<EnvironmentRecordImportSummary>;
|
|
663
|
+
cancelImport: (importId: string) => Promise<RecordImport>;
|
|
664
|
+
getAwaitingCount: () => Promise<number>;
|
|
665
|
+
};
|
|
666
|
+
get feedback(): {
|
|
667
|
+
list: () => Promise<EnvironmentFeedbackRecord[]>;
|
|
668
|
+
};
|
|
669
|
+
/**
|
|
670
|
+
* Return a plain JS copy of the synced session heap.
|
|
671
|
+
*/
|
|
672
|
+
getHeap(): SessionHeapSnapshot;
|
|
673
|
+
private sessionDataRequest;
|
|
674
|
+
private collectAllSessionItems;
|
|
675
|
+
/**
|
|
676
|
+
* Fetch the live session document from the runtime DO.
|
|
677
|
+
*
|
|
678
|
+
* For history and saved artifacts, prefer the collection APIs on
|
|
679
|
+
* `messages`, `timeline`, `jobs`, and `heap`.
|
|
680
|
+
*/
|
|
681
|
+
getDocument(): Promise<SessionDocumentResult>;
|
|
682
|
+
get messages(): {
|
|
683
|
+
list: (options?: SessionCollectionListOptions) => Promise<SessionCollectionListResult<SessionConversationMessage>>;
|
|
684
|
+
};
|
|
685
|
+
get timeline(): {
|
|
686
|
+
list: (options?: SessionCollectionListOptions) => Promise<SessionCollectionListResult<SessionTimelineEvent>>;
|
|
687
|
+
};
|
|
688
|
+
get jobs(): {
|
|
689
|
+
list: (options?: SessionJobListOptions) => Promise<SessionCollectionListResult<SessionJobRecord>>;
|
|
690
|
+
get: (jobId: string) => Promise<SessionJobRecord>;
|
|
691
|
+
};
|
|
692
|
+
get heap(): {
|
|
693
|
+
entries: {
|
|
694
|
+
list: (options?: SessionCollectionListOptions) => Promise<SessionCollectionListResult<SessionHeapEntry>>;
|
|
695
|
+
get: (path: string) => Promise<SessionHeapEntry>;
|
|
696
|
+
};
|
|
697
|
+
lists: {
|
|
698
|
+
list: (options?: SessionCollectionListOptions) => Promise<SessionCollectionListResult<SessionHeapList>>;
|
|
699
|
+
get: (name: string) => Promise<SessionHeapList>;
|
|
700
|
+
};
|
|
701
|
+
variables: {
|
|
702
|
+
list: (options?: SessionCollectionListOptions) => Promise<SessionCollectionListResult<SessionHeapVariable>>;
|
|
703
|
+
get: (name: string) => Promise<SessionHeapVariable>;
|
|
704
|
+
delete: (name: string) => Promise<{
|
|
705
|
+
deleted: boolean;
|
|
706
|
+
name: string;
|
|
707
|
+
}>;
|
|
708
|
+
};
|
|
709
|
+
};
|
|
710
|
+
get transcript(): {
|
|
711
|
+
list: (options?: SessionCollectionListOptions) => Promise<SessionCollectionListResult<SessionTranscriptEntry>>;
|
|
712
|
+
};
|
|
713
|
+
graphql<T = any>(query: string, variables?: Record<string, any>): Promise<GraphQLResult<T>>;
|
|
714
|
+
searchRecords(query: string, options?: RecordSearchOptions): Promise<RecordSearchResult[]>;
|
|
715
|
+
mentionRecord(input: RecordMentionInput): Promise<SessionHeapEntry>;
|
|
716
|
+
defineRelationship(options: DefineRelationshipOptions): Promise<RelationshipInfo>;
|
|
717
|
+
getRelationships(modelPath: string): Promise<RelationshipInfo[]>;
|
|
718
|
+
attach(modelPath: string, submodelPath: string, targetPath: string): Promise<void>;
|
|
719
|
+
detach(modelPath: string, submodelPath: string, targetPath?: string): Promise<void>;
|
|
720
|
+
listRelated(modelPath: string, submodelPath: string): Promise<ModelRef[]>;
|
|
721
|
+
applyManifest(manifest: ManifestContent): Promise<{
|
|
722
|
+
applied: number;
|
|
723
|
+
errors: string[];
|
|
724
|
+
}>;
|
|
725
|
+
recordObject(options: RecordObjectOptions): Promise<RecordObjectResult>;
|
|
726
|
+
recordObjects(records: RecordObjectOptions[], options?: RecordObjectsOptions): Promise<RecordObjectResult[]>;
|
|
727
|
+
enqueueRecordImport(records: RecordObjectOptions[], options?: RecordImportOptions): Promise<RecordImport>;
|
|
728
|
+
listRecordImports(status?: RecordImportStatus): Promise<RecordImport[]>;
|
|
729
|
+
getRecordImportSummary(): Promise<EnvironmentRecordImportSummary>;
|
|
730
|
+
getAwaitingRecordCount(): Promise<number>;
|
|
731
|
+
getRecordImport(importId: string): Promise<RecordImport>;
|
|
732
|
+
cancelRecordImport(importId: string): Promise<RecordImport>;
|
|
733
|
+
listFeedback(): Promise<EnvironmentFeedbackRecord[]>;
|
|
734
|
+
/**
|
|
735
|
+
* Close the session and disconnect from the sandbox.
|
|
736
|
+
*
|
|
737
|
+
* Sends `client.goodbye` over WebSocket first, then issues an HTTP fallback
|
|
738
|
+
* to the runtime goodbye endpoint if no definitive WS-side runtime notify
|
|
739
|
+
* acknowledgement was observed.
|
|
740
|
+
*/
|
|
741
|
+
disconnect(): Promise<void>;
|
|
742
|
+
/**
|
|
743
|
+
* Close only the socket transport without sending `client.goodbye`.
|
|
744
|
+
*/
|
|
745
|
+
disconnectTransport(): void;
|
|
746
|
+
/**
|
|
747
|
+
* Backwards-compatible alias for `disconnect()`.
|
|
748
|
+
*/
|
|
749
|
+
close(): Promise<void>;
|
|
750
|
+
/**
|
|
751
|
+
* Check if the graph container is ready and warm.
|
|
752
|
+
*/
|
|
753
|
+
checkReadiness(): Promise<{
|
|
754
|
+
lastKeepAliveAt: number;
|
|
755
|
+
status: "warming" | "hot" | "unknown";
|
|
756
|
+
}>;
|
|
757
|
+
}
|
|
758
|
+
declare class OntologyHandle {
|
|
759
|
+
private granular;
|
|
760
|
+
private ontologyNameOrId;
|
|
761
|
+
constructor(granular: Granular, ontologyNameOrId: string);
|
|
762
|
+
get effects(): {
|
|
763
|
+
register: (effect: ToolWithHandler) => Promise<void>;
|
|
764
|
+
registerMany: (effects: ToolWithHandler[]) => Promise<void>;
|
|
765
|
+
unregister: (name: string) => Promise<void>;
|
|
766
|
+
clear: () => Promise<void>;
|
|
767
|
+
disconnect: () => Promise<void>;
|
|
768
|
+
};
|
|
769
|
+
get importer(): {
|
|
770
|
+
onEnvironmentCreate: (handler: EnvironmentImporterHandler) => void;
|
|
771
|
+
clear: () => void;
|
|
772
|
+
};
|
|
773
|
+
}
|
|
774
|
+
declare class Granular {
|
|
775
|
+
private apiKey;
|
|
776
|
+
private apiUrl;
|
|
777
|
+
private httpUrl;
|
|
778
|
+
private tokenProvider?;
|
|
779
|
+
private WebSocketCtor?;
|
|
780
|
+
private onUnexpectedClose?;
|
|
781
|
+
private onReconnectError?;
|
|
782
|
+
private effectHostUrl?;
|
|
783
|
+
private debugHttp;
|
|
784
|
+
/** Sandbox-level effect registry: sandboxId → (effectKey@selector → ToolWithHandler) */
|
|
785
|
+
private sandboxEffects;
|
|
786
|
+
/** Live sandbox-scoped effect hosts keyed by sandboxId */
|
|
787
|
+
private sandboxEffectHosts;
|
|
788
|
+
/** In-flight host connection promises to avoid duplicate concurrent connects */
|
|
789
|
+
private sandboxEffectHostPromises;
|
|
790
|
+
/** Ontology-bound environment importer hooks keyed by the caller's ontology identifier. */
|
|
791
|
+
private ontologyImporters;
|
|
792
|
+
/** Resolved importer hooks keyed by sandboxId for fast lookups during openEnvironment(). */
|
|
793
|
+
private sandboxImporters;
|
|
794
|
+
/**
|
|
795
|
+
* Create a new Granular client
|
|
796
|
+
* @param options - Client configuration
|
|
797
|
+
*/
|
|
798
|
+
constructor(options: GranularOptions);
|
|
799
|
+
/**
|
|
800
|
+
* Return an ontology-scoped handle for effects and other ontology-level APIs.
|
|
801
|
+
*/
|
|
802
|
+
ontology(ontologyNameOrId: string): OntologyHandle;
|
|
803
|
+
registerEnvironmentImporter(ontologyNameOrId: string, handler: EnvironmentImporterHandler): void;
|
|
804
|
+
clearEnvironmentImporter(ontologyNameOrId: string): void;
|
|
805
|
+
/**
|
|
806
|
+
* Records/upserts a user and prepares them for sandbox connections
|
|
807
|
+
*
|
|
808
|
+
* @param options - User options
|
|
809
|
+
* @returns The recorded user with both `userId` and `granularId`
|
|
810
|
+
*
|
|
811
|
+
* @example
|
|
812
|
+
* ```typescript
|
|
813
|
+
* const user = await granular.recordUser({
|
|
814
|
+
* userId: 'user_123',
|
|
815
|
+
* name: 'John Doe',
|
|
816
|
+
* permissions: ['agent'],
|
|
817
|
+
* });
|
|
818
|
+
* ```
|
|
819
|
+
*/
|
|
820
|
+
recordUser(options: RecordUserOptions): Promise<User>;
|
|
821
|
+
/**
|
|
822
|
+
* Alias for `recordUser()` with user-facing naming that matches upsert semantics.
|
|
823
|
+
*/
|
|
824
|
+
upsertUser(options: RecordUserOptions): Promise<User>;
|
|
825
|
+
private resolveConnectUser;
|
|
826
|
+
/**
|
|
827
|
+
* Open or resolve an ontology environment for one user without opening a session.
|
|
828
|
+
*
|
|
829
|
+
* @example
|
|
830
|
+
* ```typescript
|
|
831
|
+
* const environment = await granular.openEnvironment({
|
|
832
|
+
* ontology: 'my-ontology',
|
|
833
|
+
* tag: 'dev',
|
|
834
|
+
* userId: 'user_123',
|
|
835
|
+
* permissions: ['agent'],
|
|
836
|
+
* });
|
|
837
|
+
*
|
|
838
|
+
* await environment.data.record({
|
|
839
|
+
* className: 'customer',
|
|
840
|
+
* id: 'acme',
|
|
841
|
+
* fields: { name: 'Acme' },
|
|
842
|
+
* });
|
|
843
|
+
*
|
|
844
|
+
* const session = await environment.sessions.create();
|
|
845
|
+
* const job = await session.submitJob(`return "hello";`);
|
|
846
|
+
* console.log(await job.result);
|
|
847
|
+
* ```
|
|
848
|
+
*/
|
|
849
|
+
openEnvironment(options: OpenEnvironmentOptions): Promise<Environment>;
|
|
850
|
+
/**
|
|
851
|
+
* Deprecated compatibility alias for `openEnvironment()`.
|
|
852
|
+
*
|
|
853
|
+
* `connect()` no longer opens a runtime session automatically.
|
|
854
|
+
*/
|
|
855
|
+
connect(options: ConnectOptions): Promise<Environment>;
|
|
856
|
+
/**
|
|
857
|
+
* Run a registered environment importer against an environment that was
|
|
858
|
+
* opened outside this SDK instance, for example by a delegated browser flow.
|
|
859
|
+
*
|
|
860
|
+
* This uses the same setup-run and queued record-import plumbing as
|
|
861
|
+
* `openEnvironment()`: importer stages, expected object counts, and queued
|
|
862
|
+
* import counters remain visible through `environment.setup` and
|
|
863
|
+
* `getRecordImportSummary()`.
|
|
864
|
+
*/
|
|
865
|
+
runEnvironmentImporterForEnvironment(environmentId: string, options?: RunEnvironmentImporterOptions): Promise<EnvironmentSetupSummary | null>;
|
|
866
|
+
private resolveRequestedTag;
|
|
867
|
+
private buildManagedEnvironmentName;
|
|
868
|
+
private matchesTagTrackedEnvironment;
|
|
869
|
+
private sortEnvironmentsByRecency;
|
|
870
|
+
private resolveOpenEnvironmentData;
|
|
871
|
+
/**
|
|
872
|
+
* List active (open) sessions for an environment — each session is one agent conversation thread.
|
|
873
|
+
*/
|
|
874
|
+
listOpenSessions(filters: {
|
|
875
|
+
environmentId: string;
|
|
876
|
+
}): Promise<ConversationSessionInfo[]>;
|
|
877
|
+
/**
|
|
878
|
+
* List closed sessions for an environment (conversations that have disconnected).
|
|
879
|
+
*/
|
|
880
|
+
listClosedSessions(filters: {
|
|
881
|
+
environmentId: string;
|
|
882
|
+
}): Promise<ConversationSessionInfo[]>;
|
|
883
|
+
private listSessionsForEnvironment;
|
|
884
|
+
private normalizeConversationSession;
|
|
885
|
+
private static coerceIsoDate;
|
|
886
|
+
/**
|
|
887
|
+
* Create a new session (conversation) for an existing environment and connect to it.
|
|
888
|
+
* The runtime graph is shared across all sessions for the same environment.
|
|
889
|
+
*/
|
|
890
|
+
createSession(options: {
|
|
891
|
+
environmentId: string;
|
|
892
|
+
clientId?: string;
|
|
893
|
+
initialHeap?: CreateSessionOptions["initialHeap"];
|
|
894
|
+
}): Promise<EnvironmentSession>;
|
|
895
|
+
/**
|
|
896
|
+
* Connect to an existing open session (same conversation thread) using a freshly minted WebSocket token.
|
|
897
|
+
*/
|
|
898
|
+
connectSession(options: {
|
|
899
|
+
sessionId: string;
|
|
900
|
+
clientId?: string;
|
|
901
|
+
}): Promise<EnvironmentSession>;
|
|
902
|
+
recordOpenAIUsageSpend(usage: OpenAIUsageSpendEvent, context?: GranularSpendContext | null, options?: {
|
|
903
|
+
metadata?: Record<string, unknown> | null;
|
|
904
|
+
}): Promise<RecordOpenAIUsageSpendResult>;
|
|
905
|
+
/**
|
|
906
|
+
* Mark a session closed in the control plane. If `environment` is the connected handle for that
|
|
907
|
+
* `sessionId`, disconnects the WebSocket so the runtime tears down cleanly.
|
|
908
|
+
*/
|
|
909
|
+
closeSession(sessionId: string, environment?: EnvironmentSession | null): Promise<void>;
|
|
910
|
+
/**
|
|
911
|
+
* Re-open a closed session in the index and connect to its existing runtime document.
|
|
912
|
+
*/
|
|
913
|
+
reopenSession(sessionId: string, options?: {
|
|
914
|
+
clientId?: string;
|
|
915
|
+
}): Promise<EnvironmentSession>;
|
|
916
|
+
private resolveEnvironmentImporter;
|
|
917
|
+
private maybeRunEnvironmentImporter;
|
|
918
|
+
private runEnvironmentImporter;
|
|
919
|
+
private bindEnvironmentHandle;
|
|
920
|
+
private bindWebSocketEnvironmentSession;
|
|
921
|
+
private activateEnvironment;
|
|
922
|
+
private getSandboxEffectMap;
|
|
923
|
+
private serializeEffect;
|
|
924
|
+
private publishSandboxEffectCatalog;
|
|
925
|
+
private syncSandboxEffectCatalog;
|
|
926
|
+
private recoverEffectHost;
|
|
927
|
+
private startEffectHostHeartbeat;
|
|
928
|
+
private stopEffectHostHeartbeat;
|
|
929
|
+
private synchronizeEffectHost;
|
|
930
|
+
private ensureSandboxEffectHost;
|
|
931
|
+
private disconnectSandboxEffectHost;
|
|
932
|
+
/**
|
|
933
|
+
* Register an effect (tool) for a specific sandbox.
|
|
934
|
+
*
|
|
935
|
+
* @param sandboxNameOrId - The name or ID of the sandbox
|
|
936
|
+
* @param effect - The tool definition and handler
|
|
937
|
+
*/
|
|
938
|
+
registerEffect(sandboxNameOrId: string, effect: ToolWithHandler): Promise<void>;
|
|
939
|
+
/**
|
|
940
|
+
* Register multiple effects (tools) for a specific sandbox.
|
|
941
|
+
*
|
|
942
|
+
* batch version of `registerEffect`.
|
|
943
|
+
*/
|
|
944
|
+
registerEffects(sandboxNameOrId: string, effects: ToolWithHandler[]): Promise<void>;
|
|
945
|
+
/**
|
|
946
|
+
* Unregister an effect from a sandbox.
|
|
947
|
+
*
|
|
948
|
+
* Removes it from the local sandbox registry and updates the
|
|
949
|
+
* sandbox-scoped live catalog.
|
|
950
|
+
*/
|
|
951
|
+
unregisterEffect(sandboxNameOrId: string, name: string): Promise<void>;
|
|
952
|
+
/**
|
|
953
|
+
* Disconnect one sandbox-scoped effect host, or all of them when no sandbox is provided.
|
|
954
|
+
*
|
|
955
|
+
* This is primarily useful for long-lived helper processes such as generated
|
|
956
|
+
* `granular-effects.ts` scripts that need to shut down cleanly on SIGINT/SIGTERM.
|
|
957
|
+
*/
|
|
958
|
+
disconnectEffects(sandboxNameOrId?: string): Promise<void>;
|
|
959
|
+
/**
|
|
960
|
+
* Unregister all effects for a sandbox.
|
|
961
|
+
*/
|
|
962
|
+
unregisterAllEffects(sandboxNameOrId: string): Promise<void>;
|
|
963
|
+
/**
|
|
964
|
+
* Find a sandbox by name or create it if it doesn't exist
|
|
965
|
+
*/
|
|
966
|
+
private findOrCreateSandbox;
|
|
967
|
+
/**
|
|
968
|
+
* Ensure a permission profile exists for a sandbox, creating it if needed.
|
|
969
|
+
* If profileName matches an existing profile name, returns its ID.
|
|
970
|
+
* Otherwise, creates a v1 source-profile file shape with an allow default.
|
|
971
|
+
*/
|
|
972
|
+
private ensurePermissionProfile;
|
|
973
|
+
/**
|
|
974
|
+
* Ensure an assignment exists for a subject in a sandbox with a permission profile
|
|
975
|
+
*/
|
|
976
|
+
private ensureAssignment;
|
|
977
|
+
/**
|
|
978
|
+
* Sandbox management API
|
|
979
|
+
*/
|
|
980
|
+
get sandboxes(): {
|
|
981
|
+
list: () => Promise<SandboxListResponse>;
|
|
982
|
+
get: (id: string) => Promise<Sandbox>;
|
|
983
|
+
create: (data: CreateSandboxData) => Promise<Sandbox>;
|
|
984
|
+
update: (id: string, data: Partial<CreateSandboxData>) => Promise<Sandbox>;
|
|
985
|
+
delete: (id: string) => Promise<DeleteResponse>;
|
|
986
|
+
};
|
|
987
|
+
/**
|
|
988
|
+
* Permission Profile management for sandboxes
|
|
989
|
+
*/
|
|
990
|
+
get permissionProfiles(): {
|
|
991
|
+
list: (sandboxId: string) => Promise<PermissionProfile[]>;
|
|
992
|
+
get: (sandboxId: string, profileId: string) => Promise<PermissionProfile>;
|
|
993
|
+
create: (sandboxId: string, data: CreatePermissionProfileData) => Promise<PermissionProfile>;
|
|
994
|
+
delete: (_sandboxId: string, _profileId: string) => Promise<DeleteResponse>;
|
|
995
|
+
};
|
|
996
|
+
/**
|
|
997
|
+
* Environment management
|
|
998
|
+
*/
|
|
999
|
+
get environments(): {
|
|
1000
|
+
list: (sandboxId: string) => Promise<EnvironmentData[]>;
|
|
1001
|
+
get: (environmentId: string) => Promise<EnvironmentData>;
|
|
1002
|
+
create: (sandboxId: string, data: CreateEnvironmentData) => Promise<EnvironmentData>;
|
|
1003
|
+
delete: (environmentId: string) => Promise<DeleteResponse>;
|
|
1004
|
+
};
|
|
1005
|
+
/**
|
|
1006
|
+
* Event stream operations: query, subscribe, and acknowledge stream events
|
|
1007
|
+
*/
|
|
1008
|
+
get streams(): {
|
|
1009
|
+
getEvents: (params: {
|
|
1010
|
+
ontology: string;
|
|
1011
|
+
stream: string;
|
|
1012
|
+
environment?: string;
|
|
1013
|
+
session?: string;
|
|
1014
|
+
eventTypes?: string[];
|
|
1015
|
+
since?: Date;
|
|
1016
|
+
until?: Date;
|
|
1017
|
+
isAcked?: boolean;
|
|
1018
|
+
limit?: number;
|
|
1019
|
+
offset?: number;
|
|
1020
|
+
}) => Promise<StreamEvent[]>;
|
|
1021
|
+
subscribe: (params: {
|
|
1022
|
+
ontology: string;
|
|
1023
|
+
stream: string;
|
|
1024
|
+
environment?: string;
|
|
1025
|
+
session?: string;
|
|
1026
|
+
eventTypes?: string[];
|
|
1027
|
+
since?: Date;
|
|
1028
|
+
onEvent: (event: StreamEvent) => void;
|
|
1029
|
+
onError?: (err: Error) => void;
|
|
1030
|
+
pollIntervalMs?: number;
|
|
1031
|
+
}) => StreamSubscription;
|
|
1032
|
+
ack: (eventId: string) => Promise<void>;
|
|
1033
|
+
ackBatch: (eventIds: string[]) => Promise<void>;
|
|
1034
|
+
getStats: (params: {
|
|
1035
|
+
ontology: string;
|
|
1036
|
+
environment?: string;
|
|
1037
|
+
}) => Promise<StreamStats[]>;
|
|
1038
|
+
};
|
|
1039
|
+
/**
|
|
1040
|
+
* Subject management
|
|
1041
|
+
*/
|
|
1042
|
+
get subjects(): {
|
|
1043
|
+
get: (subjectId: string) => Promise<Subject>;
|
|
1044
|
+
listAssignments: (subjectId: string) => Promise<AssignmentListResponse>;
|
|
1045
|
+
};
|
|
1046
|
+
/**
|
|
1047
|
+
* @deprecated Use recordUser() instead
|
|
1048
|
+
*/
|
|
1049
|
+
get users(): {
|
|
1050
|
+
create: (data: {
|
|
1051
|
+
id: string;
|
|
1052
|
+
name?: string;
|
|
1053
|
+
email?: string;
|
|
1054
|
+
}) => Promise<Subject>;
|
|
1055
|
+
get: (id: string) => Promise<Subject>;
|
|
1056
|
+
};
|
|
1057
|
+
private _resolveSandboxId;
|
|
1058
|
+
/**
|
|
1059
|
+
* Make an authenticated API request
|
|
1060
|
+
*/
|
|
1061
|
+
private request;
|
|
1062
|
+
}
|
|
1063
|
+
|
|
1064
|
+
export { Environment as E, Granular as G, OntologyHandle as O, Session as S, WSClient as W, EnvironmentSession as a };
|