@granular-software/sdk 0.4.30 → 0.4.31
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 +25 -20
- package/dist/agent-evals.d.mts +7 -6
- package/dist/agent-evals.d.ts +7 -6
- package/dist/agent-evals.js +681 -277
- package/dist/agent-evals.js.map +1 -1
- package/dist/agent-evals.mjs +681 -277
- package/dist/agent-evals.mjs.map +1 -1
- package/dist/cli/index.js +526 -271
- package/dist/{client-BVvOMfln.d.mts → client-CTH1hfwe.d.mts} +254 -123
- package/dist/{client-BVvOMfln.d.ts → client-CTH1hfwe.d.ts} +254 -123
- package/dist/index.d.mts +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js +439 -187
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +438 -188
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/index.mjs
CHANGED
|
@@ -4647,27 +4647,27 @@ var Session = class {
|
|
|
4647
4647
|
}
|
|
4648
4648
|
async publishTools(tools, revision = "1.0.0") {
|
|
4649
4649
|
throw new Error(
|
|
4650
|
-
"Environment-scoped effect publication was removed. Declare effects in the manifest and register live handlers with granular.
|
|
4650
|
+
"Environment-scoped effect publication was removed. Declare effects in the manifest and register live handlers with granular.ontology(environment.sandboxId).effects.registerMany(effects)."
|
|
4651
4651
|
);
|
|
4652
4652
|
}
|
|
4653
4653
|
async publishEffect(effect) {
|
|
4654
4654
|
throw new Error(
|
|
4655
|
-
"Environment-scoped effect publication was removed. Use granular.
|
|
4655
|
+
"Environment-scoped effect publication was removed. Use granular.ontology(environment.sandboxId).effects.register(effect)."
|
|
4656
4656
|
);
|
|
4657
4657
|
}
|
|
4658
4658
|
async publishEffects(effects) {
|
|
4659
4659
|
throw new Error(
|
|
4660
|
-
"Environment-scoped effect publication was removed. Use granular.
|
|
4660
|
+
"Environment-scoped effect publication was removed. Use granular.ontology(environment.sandboxId).effects.registerMany(effects)."
|
|
4661
4661
|
);
|
|
4662
4662
|
}
|
|
4663
4663
|
async unpublishEffect(name) {
|
|
4664
4664
|
throw new Error(
|
|
4665
|
-
"Environment-scoped effect publication was removed. Use granular.
|
|
4665
|
+
"Environment-scoped effect publication was removed. Use granular.ontology(environment.sandboxId).effects.unregister(effectName)."
|
|
4666
4666
|
);
|
|
4667
4667
|
}
|
|
4668
4668
|
async unpublishAllEffects() {
|
|
4669
4669
|
throw new Error(
|
|
4670
|
-
"Environment-scoped effect publication was removed. Use granular.
|
|
4670
|
+
"Environment-scoped effect publication was removed. Use granular.ontology(environment.sandboxId).effects.clear()."
|
|
4671
4671
|
);
|
|
4672
4672
|
}
|
|
4673
4673
|
/**
|
|
@@ -11691,6 +11691,22 @@ function normalizeHeapSnapshot(raw) {
|
|
|
11691
11691
|
updatedAt: typeof heap.updatedAt === "number" ? heap.updatedAt : Date.now()
|
|
11692
11692
|
};
|
|
11693
11693
|
}
|
|
11694
|
+
function deriveRuntimeBaseUrl(apiEndpoint) {
|
|
11695
|
+
try {
|
|
11696
|
+
const endpoint = new URL(apiEndpoint);
|
|
11697
|
+
const graphqlSuffix = "/orchestrator/graphql";
|
|
11698
|
+
if (endpoint.pathname.endsWith(graphqlSuffix)) {
|
|
11699
|
+
endpoint.pathname = endpoint.pathname.slice(0, -graphqlSuffix.length);
|
|
11700
|
+
} else if (endpoint.pathname.endsWith("/graphql")) {
|
|
11701
|
+
endpoint.pathname = endpoint.pathname.slice(0, -"/graphql".length);
|
|
11702
|
+
}
|
|
11703
|
+
endpoint.search = "";
|
|
11704
|
+
endpoint.hash = "";
|
|
11705
|
+
return endpoint.toString().replace(/\/$/, "");
|
|
11706
|
+
} catch {
|
|
11707
|
+
return apiEndpoint.replace(/\/orchestrator\/graphql$/, "").replace(/\/$/, "");
|
|
11708
|
+
}
|
|
11709
|
+
}
|
|
11694
11710
|
function normalizeSubject(subject) {
|
|
11695
11711
|
const granularId = subject.granularId || subject.subjectId;
|
|
11696
11712
|
const userId = subject.userId || subject.identityId || granularId;
|
|
@@ -11730,12 +11746,13 @@ function normalizeEnvironmentData(environment) {
|
|
|
11730
11746
|
tracking: environment.tracking || buildPolicy
|
|
11731
11747
|
};
|
|
11732
11748
|
}
|
|
11733
|
-
var Environment = class
|
|
11749
|
+
var Environment = class {
|
|
11750
|
+
granular;
|
|
11734
11751
|
envData;
|
|
11735
11752
|
_apiKey;
|
|
11736
11753
|
_apiEndpoint;
|
|
11737
|
-
constructor(
|
|
11738
|
-
|
|
11754
|
+
constructor(granular, envData, apiKey, apiEndpoint) {
|
|
11755
|
+
this.granular = granular;
|
|
11739
11756
|
this.envData = envData;
|
|
11740
11757
|
this._apiKey = apiKey;
|
|
11741
11758
|
this._apiEndpoint = apiEndpoint;
|
|
@@ -11776,35 +11793,126 @@ var Environment = class extends Session {
|
|
|
11776
11793
|
get permissionProfileId() {
|
|
11777
11794
|
return this.envData.permissionProfileId;
|
|
11778
11795
|
}
|
|
11796
|
+
/** The current build policy backing this environment */
|
|
11797
|
+
get buildPolicy() {
|
|
11798
|
+
return this.envData.buildPolicy;
|
|
11799
|
+
}
|
|
11800
|
+
/** The current update state relative to the followed tag */
|
|
11801
|
+
get updateState() {
|
|
11802
|
+
return this.envData.updateState;
|
|
11803
|
+
}
|
|
11804
|
+
/** Convenience flag for whether this environment trails the current tag target */
|
|
11805
|
+
get isOutdated() {
|
|
11806
|
+
return this.envData.updateState === "update_available";
|
|
11807
|
+
}
|
|
11808
|
+
/** The followed tag name when this environment is tag-tracked */
|
|
11809
|
+
get tag() {
|
|
11810
|
+
return this.envData.tag?.name || this.envData.buildPolicy.tagName || null;
|
|
11811
|
+
}
|
|
11779
11812
|
/** The GraphQL API endpoint URL */
|
|
11780
11813
|
get apiEndpoint() {
|
|
11781
11814
|
return this._apiEndpoint;
|
|
11782
11815
|
}
|
|
11816
|
+
/** Internal auth token used for control-plane and runtime fallback requests */
|
|
11817
|
+
get authToken() {
|
|
11818
|
+
return this._apiKey;
|
|
11819
|
+
}
|
|
11820
|
+
/** Base runtime URL derived from the GraphQL endpoint */
|
|
11821
|
+
get runtimeBaseUrl() {
|
|
11822
|
+
return this.getRuntimeBaseUrl();
|
|
11823
|
+
}
|
|
11824
|
+
get sessions() {
|
|
11825
|
+
return {
|
|
11826
|
+
list: async (options) => this.listSessions(options?.status || "active"),
|
|
11827
|
+
create: async (options) => this.createSession(options),
|
|
11828
|
+
connect: async (sessionId, options) => this.connectSession(sessionId, options),
|
|
11829
|
+
reopen: async (sessionId, options) => this.reopenSession(sessionId, options),
|
|
11830
|
+
close: async (sessionId, session) => this.closeSession(sessionId, session)
|
|
11831
|
+
};
|
|
11832
|
+
}
|
|
11833
|
+
get data() {
|
|
11834
|
+
return {
|
|
11835
|
+
record: async (record) => this.recordObject(record),
|
|
11836
|
+
recordMany: async (records, options) => this.recordObjects(records, options),
|
|
11837
|
+
import: async (records, options) => this.enqueueRecordImport(records, options),
|
|
11838
|
+
listImports: async (status) => this.listRecordImports(status),
|
|
11839
|
+
getImport: async (importId) => this.getRecordImport(importId),
|
|
11840
|
+
getImportSummary: async () => this.getRecordImportSummary(),
|
|
11841
|
+
cancelImport: async (importId) => this.cancelRecordImport(importId),
|
|
11842
|
+
getAwaitingCount: async () => this.getAwaitingRecordCount()
|
|
11843
|
+
};
|
|
11844
|
+
}
|
|
11845
|
+
get feedback() {
|
|
11846
|
+
return {
|
|
11847
|
+
list: async () => this.listFeedback()
|
|
11848
|
+
};
|
|
11849
|
+
}
|
|
11783
11850
|
/**
|
|
11784
|
-
*
|
|
11785
|
-
*
|
|
11786
|
-
*
|
|
11787
|
-
*
|
|
11851
|
+
* Sessionless environments do not own a live transport, so disconnecting the
|
|
11852
|
+
* environment handle itself is a no-op. This keeps the public surface
|
|
11853
|
+
* symmetric with `EnvironmentSession.disconnect()` and lets callers always
|
|
11854
|
+
* clean up safely without tracking whether they currently hold an environment
|
|
11855
|
+
* or a session.
|
|
11788
11856
|
*/
|
|
11789
|
-
|
|
11790
|
-
const doc = this.document;
|
|
11791
|
-
return normalizeHeapSnapshot(doc?.heap);
|
|
11857
|
+
async disconnect() {
|
|
11792
11858
|
}
|
|
11793
|
-
|
|
11794
|
-
|
|
11795
|
-
const
|
|
11796
|
-
|
|
11797
|
-
|
|
11798
|
-
|
|
11799
|
-
|
|
11800
|
-
|
|
11801
|
-
|
|
11802
|
-
endpoint.search = "";
|
|
11803
|
-
endpoint.hash = "";
|
|
11804
|
-
return endpoint.toString().replace(/\/$/, "");
|
|
11805
|
-
} catch {
|
|
11806
|
-
return this._apiEndpoint.replace(/\/orchestrator\/graphql$/, "").replace(/\/$/, "");
|
|
11859
|
+
async listSessions(status = "active") {
|
|
11860
|
+
if (status === "all") {
|
|
11861
|
+
const [active, closed] = await Promise.all([
|
|
11862
|
+
this.granular.listOpenSessions({ environmentId: this.environmentId }),
|
|
11863
|
+
this.granular.listClosedSessions({ environmentId: this.environmentId })
|
|
11864
|
+
]);
|
|
11865
|
+
return [...active, ...closed].sort(
|
|
11866
|
+
(left, right) => Date.parse(right.lastSeenAt) - Date.parse(left.lastSeenAt)
|
|
11867
|
+
);
|
|
11807
11868
|
}
|
|
11869
|
+
return status === "closed" ? this.granular.listClosedSessions({ environmentId: this.environmentId }) : this.granular.listOpenSessions({ environmentId: this.environmentId });
|
|
11870
|
+
}
|
|
11871
|
+
async createSession(options) {
|
|
11872
|
+
return this.granular.createSession({
|
|
11873
|
+
environmentId: this.environmentId,
|
|
11874
|
+
clientId: options?.clientId,
|
|
11875
|
+
initialHeap: options?.initialHeap
|
|
11876
|
+
});
|
|
11877
|
+
}
|
|
11878
|
+
async connectSession(sessionId, options) {
|
|
11879
|
+
const session = await this.granular["connectSession"]({
|
|
11880
|
+
sessionId,
|
|
11881
|
+
clientId: options?.clientId
|
|
11882
|
+
});
|
|
11883
|
+
if (session.environmentId !== this.environmentId) {
|
|
11884
|
+
await session.disconnect().catch(() => {
|
|
11885
|
+
session.disconnectTransport();
|
|
11886
|
+
});
|
|
11887
|
+
throw new Error(
|
|
11888
|
+
`Session ${sessionId} belongs to environment ${session.environmentId}, not ${this.environmentId}.`
|
|
11889
|
+
);
|
|
11890
|
+
}
|
|
11891
|
+
return session;
|
|
11892
|
+
}
|
|
11893
|
+
async reopenSession(sessionId, options) {
|
|
11894
|
+
const session = await this.granular.reopenSession(sessionId, {
|
|
11895
|
+
clientId: options?.clientId
|
|
11896
|
+
});
|
|
11897
|
+
if (session.environmentId !== this.environmentId) {
|
|
11898
|
+
await session.disconnect().catch(() => {
|
|
11899
|
+
session.disconnectTransport();
|
|
11900
|
+
});
|
|
11901
|
+
throw new Error(
|
|
11902
|
+
`Session ${sessionId} belongs to environment ${session.environmentId}, not ${this.environmentId}.`
|
|
11903
|
+
);
|
|
11904
|
+
}
|
|
11905
|
+
return session;
|
|
11906
|
+
}
|
|
11907
|
+
async closeSession(sessionId, session) {
|
|
11908
|
+
await this.granular.closeSession(sessionId, session);
|
|
11909
|
+
}
|
|
11910
|
+
async listFeedback() {
|
|
11911
|
+
const response = await this.controlPlaneRequest(`/control/environments/${this.environmentId}/feedback`);
|
|
11912
|
+
return Array.isArray(response.items) ? response.items : [];
|
|
11913
|
+
}
|
|
11914
|
+
getRuntimeBaseUrl() {
|
|
11915
|
+
return deriveRuntimeBaseUrl(this._apiEndpoint);
|
|
11808
11916
|
}
|
|
11809
11917
|
async controlPlaneRequest(path, options = {}) {
|
|
11810
11918
|
const runtimeBase = this.getRuntimeBaseUrl();
|
|
@@ -11825,95 +11933,6 @@ var Environment = class extends Session {
|
|
|
11825
11933
|
}
|
|
11826
11934
|
return response.json();
|
|
11827
11935
|
}
|
|
11828
|
-
/**
|
|
11829
|
-
* Close the session and disconnect from the sandbox.
|
|
11830
|
-
*
|
|
11831
|
-
* Sends `client.goodbye` over WebSocket first, then issues an HTTP fallback
|
|
11832
|
-
* to the runtime goodbye endpoint if no definitive WS-side runtime notify
|
|
11833
|
-
* acknowledgement was observed.
|
|
11834
|
-
*/
|
|
11835
|
-
async disconnect() {
|
|
11836
|
-
let wsNotifiedRuntime = false;
|
|
11837
|
-
try {
|
|
11838
|
-
const goodbye = await this.rpc(
|
|
11839
|
-
"client.goodbye",
|
|
11840
|
-
{
|
|
11841
|
-
timestamp: Date.now()
|
|
11842
|
-
}
|
|
11843
|
-
);
|
|
11844
|
-
wsNotifiedRuntime = Boolean(goodbye?.ok && goodbye?.via);
|
|
11845
|
-
} catch {
|
|
11846
|
-
wsNotifiedRuntime = false;
|
|
11847
|
-
}
|
|
11848
|
-
if (!wsNotifiedRuntime) {
|
|
11849
|
-
try {
|
|
11850
|
-
const runtimeBase = this.getRuntimeBaseUrl();
|
|
11851
|
-
await fetch(
|
|
11852
|
-
`${runtimeBase}/orchestrator/runtime/environments/${this.environmentId}/session-goodbye`,
|
|
11853
|
-
{
|
|
11854
|
-
method: "POST",
|
|
11855
|
-
headers: {
|
|
11856
|
-
"Content-Type": "application/json",
|
|
11857
|
-
Authorization: `Bearer ${this._apiKey}`,
|
|
11858
|
-
Connection: "close"
|
|
11859
|
-
},
|
|
11860
|
-
body: JSON.stringify({
|
|
11861
|
-
reason: "sdk_disconnect_http_fallback",
|
|
11862
|
-
sessionId: this.client.currentSessionId
|
|
11863
|
-
})
|
|
11864
|
-
}
|
|
11865
|
-
);
|
|
11866
|
-
} catch {
|
|
11867
|
-
}
|
|
11868
|
-
}
|
|
11869
|
-
this.client.disconnect();
|
|
11870
|
-
}
|
|
11871
|
-
/**
|
|
11872
|
-
* Close only the socket transport without sending `client.goodbye`.
|
|
11873
|
-
*
|
|
11874
|
-
* Use this when the caller intends to immediately reattach to the same
|
|
11875
|
-
* session after an unexpected disconnect.
|
|
11876
|
-
*/
|
|
11877
|
-
disconnectTransport() {
|
|
11878
|
-
this.client.disconnect();
|
|
11879
|
-
}
|
|
11880
|
-
// ==================== GRAPH CONTAINER READINESS ====================
|
|
11881
|
-
/** The last known graph container status, updated by checkReadiness() or on heartbeat */
|
|
11882
|
-
graphContainerStatus = null;
|
|
11883
|
-
/**
|
|
11884
|
-
* Check if the graph container is ready and warm.
|
|
11885
|
-
*
|
|
11886
|
-
* Sends a lightweight heartbeat RPC to the Session DO which internally
|
|
11887
|
-
* pings the FalkorDB container. The response includes `graphContainerStatus`,
|
|
11888
|
-
* which is stored locally and emitted as a `readiness` event.
|
|
11889
|
-
*
|
|
11890
|
-
* Use this method to proactively warm the graph container before any
|
|
11891
|
-
* GraphQL query that requires it, or to poll the container's state in
|
|
11892
|
-
* the background.
|
|
11893
|
-
*
|
|
11894
|
-
* @returns The current graph container status object
|
|
11895
|
-
*
|
|
11896
|
-
* @example
|
|
11897
|
-
* ```typescript
|
|
11898
|
-
* const status = await env.checkReadiness();
|
|
11899
|
-
* console.log(status.status); // 'hot' | 'warming' | 'unknown'
|
|
11900
|
-
*
|
|
11901
|
-
* // Or listen for live updates
|
|
11902
|
-
* env.on('readiness', (status) => {
|
|
11903
|
-
* console.log('Graph is now:', status.status);
|
|
11904
|
-
* });
|
|
11905
|
-
* ```
|
|
11906
|
-
*/
|
|
11907
|
-
async checkReadiness() {
|
|
11908
|
-
const result = await this.client.call("client.heartbeat", {});
|
|
11909
|
-
const containerStatus = result?.graphContainerStatus ?? {
|
|
11910
|
-
lastKeepAliveAt: Date.now(),
|
|
11911
|
-
status: "unknown"
|
|
11912
|
-
};
|
|
11913
|
-
this.graphContainerStatus = containerStatus;
|
|
11914
|
-
this.emit("readiness", containerStatus);
|
|
11915
|
-
return containerStatus;
|
|
11916
|
-
}
|
|
11917
11936
|
// ==================== ID ↔ GRAPH PATH MAPPING ====================
|
|
11918
11937
|
/**
|
|
11919
11938
|
* Convert a class name + real-world ID into a unique graph path.
|
|
@@ -12774,36 +12793,186 @@ var Environment = class extends Session {
|
|
|
12774
12793
|
}
|
|
12775
12794
|
);
|
|
12776
12795
|
}
|
|
12777
|
-
|
|
12796
|
+
};
|
|
12797
|
+
var EnvironmentSession = class extends Session {
|
|
12798
|
+
environment;
|
|
12799
|
+
/** The last known graph container status, updated by checkReadiness() or on heartbeat */
|
|
12800
|
+
graphContainerStatus = null;
|
|
12801
|
+
constructor(client, environment, clientId) {
|
|
12802
|
+
super(client, clientId);
|
|
12803
|
+
this.environment = environment;
|
|
12804
|
+
}
|
|
12805
|
+
get environmentId() {
|
|
12806
|
+
return this.environment.environmentId;
|
|
12807
|
+
}
|
|
12808
|
+
get sandboxId() {
|
|
12809
|
+
return this.environment.sandboxId;
|
|
12810
|
+
}
|
|
12811
|
+
get ontologyId() {
|
|
12812
|
+
return this.environment.ontologyId;
|
|
12813
|
+
}
|
|
12814
|
+
get subjectId() {
|
|
12815
|
+
return this.environment.subjectId;
|
|
12816
|
+
}
|
|
12817
|
+
get envName() {
|
|
12818
|
+
return this.environment.envName;
|
|
12819
|
+
}
|
|
12820
|
+
get versionId() {
|
|
12821
|
+
return this.environment.versionId;
|
|
12822
|
+
}
|
|
12823
|
+
get granularId() {
|
|
12824
|
+
return this.environment.granularId;
|
|
12825
|
+
}
|
|
12826
|
+
get permissionProfileId() {
|
|
12827
|
+
return this.environment.permissionProfileId;
|
|
12828
|
+
}
|
|
12829
|
+
get apiEndpoint() {
|
|
12830
|
+
return this.environment.apiEndpoint;
|
|
12831
|
+
}
|
|
12832
|
+
get data() {
|
|
12833
|
+
return this.environment.data;
|
|
12834
|
+
}
|
|
12835
|
+
get feedback() {
|
|
12836
|
+
return this.environment.feedback;
|
|
12837
|
+
}
|
|
12778
12838
|
/**
|
|
12779
|
-
*
|
|
12839
|
+
* Return a plain JS snapshot of the synced session heap.
|
|
12780
12840
|
*/
|
|
12781
|
-
|
|
12782
|
-
|
|
12841
|
+
getHeap() {
|
|
12842
|
+
const doc = this.document;
|
|
12843
|
+
return normalizeHeapSnapshot(doc?.heap);
|
|
12844
|
+
}
|
|
12845
|
+
async graphql(query, variables) {
|
|
12846
|
+
return this.environment.graphql(query, variables);
|
|
12847
|
+
}
|
|
12848
|
+
async defineRelationship(options) {
|
|
12849
|
+
return this.environment.defineRelationship(options);
|
|
12850
|
+
}
|
|
12851
|
+
async getRelationships(modelPath) {
|
|
12852
|
+
return this.environment.getRelationships(modelPath);
|
|
12853
|
+
}
|
|
12854
|
+
async attach(modelPath, submodelPath, targetPath) {
|
|
12855
|
+
return this.environment.attach(modelPath, submodelPath, targetPath);
|
|
12856
|
+
}
|
|
12857
|
+
async detach(modelPath, submodelPath, targetPath) {
|
|
12858
|
+
return this.environment.detach(modelPath, submodelPath, targetPath);
|
|
12859
|
+
}
|
|
12860
|
+
async listRelated(modelPath, submodelPath) {
|
|
12861
|
+
return this.environment.listRelated(modelPath, submodelPath);
|
|
12862
|
+
}
|
|
12863
|
+
async applyManifest(manifest) {
|
|
12864
|
+
return this.environment.applyManifest(manifest);
|
|
12865
|
+
}
|
|
12866
|
+
async recordObject(options) {
|
|
12867
|
+
return this.environment.recordObject(options);
|
|
12868
|
+
}
|
|
12869
|
+
async recordObjects(records, options) {
|
|
12870
|
+
return this.environment.recordObjects(records, options);
|
|
12871
|
+
}
|
|
12872
|
+
async enqueueRecordImport(records, options = {}) {
|
|
12873
|
+
return this.environment.enqueueRecordImport(records, options);
|
|
12874
|
+
}
|
|
12875
|
+
async listRecordImports(status) {
|
|
12876
|
+
return this.environment.listRecordImports(status);
|
|
12877
|
+
}
|
|
12878
|
+
async getRecordImportSummary() {
|
|
12879
|
+
return this.environment.getRecordImportSummary();
|
|
12880
|
+
}
|
|
12881
|
+
async getAwaitingRecordCount() {
|
|
12882
|
+
return this.environment.getAwaitingRecordCount();
|
|
12883
|
+
}
|
|
12884
|
+
async getRecordImport(importId) {
|
|
12885
|
+
return this.environment.getRecordImport(importId);
|
|
12886
|
+
}
|
|
12887
|
+
async cancelRecordImport(importId) {
|
|
12888
|
+
return this.environment.cancelRecordImport(importId);
|
|
12889
|
+
}
|
|
12890
|
+
async listFeedback() {
|
|
12891
|
+
return this.environment.listFeedback();
|
|
12783
12892
|
}
|
|
12784
12893
|
/**
|
|
12785
|
-
*
|
|
12894
|
+
* Close the session and disconnect from the sandbox.
|
|
12895
|
+
*
|
|
12896
|
+
* Sends `client.goodbye` over WebSocket first, then issues an HTTP fallback
|
|
12897
|
+
* to the runtime goodbye endpoint if no definitive WS-side runtime notify
|
|
12898
|
+
* acknowledgement was observed.
|
|
12786
12899
|
*/
|
|
12787
|
-
async
|
|
12788
|
-
|
|
12900
|
+
async disconnect() {
|
|
12901
|
+
let wsNotifiedRuntime = false;
|
|
12902
|
+
try {
|
|
12903
|
+
const goodbye = await this.rpc(
|
|
12904
|
+
"client.goodbye",
|
|
12905
|
+
{
|
|
12906
|
+
timestamp: Date.now()
|
|
12907
|
+
}
|
|
12908
|
+
);
|
|
12909
|
+
wsNotifiedRuntime = Boolean(goodbye?.ok && goodbye?.via);
|
|
12910
|
+
} catch {
|
|
12911
|
+
wsNotifiedRuntime = false;
|
|
12912
|
+
}
|
|
12913
|
+
if (!wsNotifiedRuntime) {
|
|
12914
|
+
try {
|
|
12915
|
+
await fetch(
|
|
12916
|
+
`${this.environment.runtimeBaseUrl}/orchestrator/runtime/environments/${this.environmentId}/session-goodbye`,
|
|
12917
|
+
{
|
|
12918
|
+
method: "POST",
|
|
12919
|
+
headers: {
|
|
12920
|
+
"Content-Type": "application/json",
|
|
12921
|
+
Authorization: `Bearer ${this.environment.authToken}`,
|
|
12922
|
+
Connection: "close"
|
|
12923
|
+
},
|
|
12924
|
+
body: JSON.stringify({
|
|
12925
|
+
reason: "sdk_disconnect_http_fallback",
|
|
12926
|
+
sessionId: this.client.currentSessionId
|
|
12927
|
+
})
|
|
12928
|
+
}
|
|
12929
|
+
);
|
|
12930
|
+
} catch {
|
|
12931
|
+
}
|
|
12932
|
+
}
|
|
12933
|
+
this.client.disconnect();
|
|
12789
12934
|
}
|
|
12790
12935
|
/**
|
|
12791
|
-
*
|
|
12936
|
+
* Close only the socket transport without sending `client.goodbye`.
|
|
12792
12937
|
*/
|
|
12793
|
-
|
|
12794
|
-
|
|
12938
|
+
disconnectTransport() {
|
|
12939
|
+
this.client.disconnect();
|
|
12795
12940
|
}
|
|
12796
12941
|
/**
|
|
12797
|
-
*
|
|
12942
|
+
* Backwards-compatible alias for `disconnect()`.
|
|
12798
12943
|
*/
|
|
12799
|
-
async
|
|
12800
|
-
|
|
12944
|
+
async close() {
|
|
12945
|
+
await this.disconnect();
|
|
12801
12946
|
}
|
|
12802
12947
|
/**
|
|
12803
|
-
*
|
|
12948
|
+
* Check if the graph container is ready and warm.
|
|
12804
12949
|
*/
|
|
12805
|
-
async
|
|
12806
|
-
|
|
12950
|
+
async checkReadiness() {
|
|
12951
|
+
const result = await this.client.call("client.heartbeat", {});
|
|
12952
|
+
const containerStatus = result?.graphContainerStatus ?? {
|
|
12953
|
+
lastKeepAliveAt: Date.now(),
|
|
12954
|
+
status: "unknown"
|
|
12955
|
+
};
|
|
12956
|
+
this.graphContainerStatus = containerStatus;
|
|
12957
|
+
this.emit("readiness", containerStatus);
|
|
12958
|
+
return containerStatus;
|
|
12959
|
+
}
|
|
12960
|
+
};
|
|
12961
|
+
var OntologyHandle = class {
|
|
12962
|
+
granular;
|
|
12963
|
+
ontologyNameOrId;
|
|
12964
|
+
constructor(granular, ontologyNameOrId) {
|
|
12965
|
+
this.granular = granular;
|
|
12966
|
+
this.ontologyNameOrId = ontologyNameOrId;
|
|
12967
|
+
}
|
|
12968
|
+
get effects() {
|
|
12969
|
+
return {
|
|
12970
|
+
register: async (effect) => this.granular.registerEffect(this.ontologyNameOrId, effect),
|
|
12971
|
+
registerMany: async (effects) => this.granular.registerEffects(this.ontologyNameOrId, effects),
|
|
12972
|
+
unregister: async (name) => this.granular.unregisterEffect(this.ontologyNameOrId, name),
|
|
12973
|
+
clear: async () => this.granular.unregisterAllEffects(this.ontologyNameOrId),
|
|
12974
|
+
disconnect: async () => this.granular.disconnectEffects(this.ontologyNameOrId)
|
|
12975
|
+
};
|
|
12807
12976
|
}
|
|
12808
12977
|
};
|
|
12809
12978
|
var Granular = class _Granular {
|
|
@@ -12840,6 +13009,12 @@ var Granular = class _Granular {
|
|
|
12840
13009
|
this.onReconnectError = options.onReconnectError;
|
|
12841
13010
|
this.httpUrl = this.apiUrl.replace(/^wss:\/\//, "https://").replace(/^ws:\/\//, "http://").replace(/\/ws$/, "");
|
|
12842
13011
|
}
|
|
13012
|
+
/**
|
|
13013
|
+
* Return an ontology-scoped handle for effects and other ontology-level APIs.
|
|
13014
|
+
*/
|
|
13015
|
+
ontology(ontologyNameOrId) {
|
|
13016
|
+
return new OntologyHandle(this, ontologyNameOrId);
|
|
13017
|
+
}
|
|
12843
13018
|
/**
|
|
12844
13019
|
* Records/upserts a user and prepares them for sandbox connections
|
|
12845
13020
|
*
|
|
@@ -12876,7 +13051,23 @@ var Granular = class _Granular {
|
|
|
12876
13051
|
permissions: options.permissions || []
|
|
12877
13052
|
});
|
|
12878
13053
|
}
|
|
13054
|
+
/**
|
|
13055
|
+
* Alias for `recordUser()` with user-facing naming that matches upsert semantics.
|
|
13056
|
+
*/
|
|
13057
|
+
async upsertUser(options) {
|
|
13058
|
+
return this.recordUser(options);
|
|
13059
|
+
}
|
|
12879
13060
|
async resolveConnectUser(options) {
|
|
13061
|
+
const providedIdentityCount = [
|
|
13062
|
+
Boolean(options.user),
|
|
13063
|
+
Boolean(options.userId),
|
|
13064
|
+
Boolean(options.granularId)
|
|
13065
|
+
].filter(Boolean).length;
|
|
13066
|
+
if (providedIdentityCount !== 1) {
|
|
13067
|
+
throw new Error(
|
|
13068
|
+
"openEnvironment() requires exactly one of userId, granularId, or a user object returned by recordUser()."
|
|
13069
|
+
);
|
|
13070
|
+
}
|
|
12880
13071
|
if (options.user) {
|
|
12881
13072
|
const user = normalizeUser(options.user);
|
|
12882
13073
|
return {
|
|
@@ -12913,56 +13104,85 @@ var Granular = class _Granular {
|
|
|
12913
13104
|
};
|
|
12914
13105
|
}
|
|
12915
13106
|
throw new Error(
|
|
12916
|
-
"
|
|
13107
|
+
"openEnvironment() requires either userId, granularId, or a user object returned by recordUser()."
|
|
12917
13108
|
);
|
|
12918
13109
|
}
|
|
12919
13110
|
/**
|
|
12920
|
-
*
|
|
12921
|
-
*
|
|
12922
|
-
* Effects are registered at the sandbox level via `granular.registerEffect()`
|
|
12923
|
-
* or `granular.registerEffects()`. Sessions pick up live availability from
|
|
12924
|
-
* the sandbox registry automatically.
|
|
12925
|
-
*
|
|
12926
|
-
* @param options - Connection options
|
|
12927
|
-
* @returns An active environment session
|
|
13111
|
+
* Open or resolve an ontology environment for one user without opening a session.
|
|
12928
13112
|
*
|
|
12929
13113
|
* @example
|
|
12930
13114
|
* ```typescript
|
|
12931
|
-
* const environment = await granular.
|
|
13115
|
+
* const environment = await granular.openEnvironment({
|
|
12932
13116
|
* ontology: 'my-ontology',
|
|
12933
|
-
*
|
|
13117
|
+
* tag: 'dev',
|
|
12934
13118
|
* userId: 'user_123',
|
|
12935
13119
|
* permissions: ['agent'],
|
|
12936
13120
|
* });
|
|
12937
13121
|
*
|
|
12938
|
-
* await
|
|
12939
|
-
*
|
|
12940
|
-
*
|
|
12941
|
-
*
|
|
12942
|
-
* handler: async () => 'Hello!',
|
|
13122
|
+
* await environment.data.record({
|
|
13123
|
+
* className: 'customer',
|
|
13124
|
+
* id: 'acme',
|
|
13125
|
+
* fields: { name: 'Acme' },
|
|
12943
13126
|
* });
|
|
12944
13127
|
*
|
|
12945
|
-
*
|
|
12946
|
-
* const job = await
|
|
12947
|
-
*
|
|
12948
|
-
* return await tools.greet({});
|
|
12949
|
-
* `);
|
|
12950
|
-
*
|
|
12951
|
-
* console.log(await job.result); // 'Hello!'
|
|
13128
|
+
* const session = await environment.sessions.create();
|
|
13129
|
+
* const job = await session.submitJob(`return "hello";`);
|
|
13130
|
+
* console.log(await job.result);
|
|
12952
13131
|
* ```
|
|
12953
13132
|
*/
|
|
13133
|
+
async openEnvironment(options) {
|
|
13134
|
+
const envData = await this.resolveOpenEnvironmentData(
|
|
13135
|
+
options,
|
|
13136
|
+
"openEnvironment"
|
|
13137
|
+
);
|
|
13138
|
+
return this.bindEnvironmentHandle(envData);
|
|
13139
|
+
}
|
|
13140
|
+
/**
|
|
13141
|
+
* Deprecated compatibility alias for `openEnvironment()`.
|
|
13142
|
+
*
|
|
13143
|
+
* `connect()` no longer opens a runtime session automatically.
|
|
13144
|
+
*/
|
|
12954
13145
|
async connect(options) {
|
|
12955
|
-
|
|
13146
|
+
return this.openEnvironment({
|
|
13147
|
+
...options,
|
|
13148
|
+
tag: this.resolveRequestedTag(options, "connect"),
|
|
13149
|
+
permissions: options.permissions || options.user?.permissions || []
|
|
13150
|
+
});
|
|
13151
|
+
}
|
|
13152
|
+
resolveRequestedTag(options, methodName) {
|
|
13153
|
+
const tag = options.tag?.trim() || options.tagName?.trim() || options.environment?.trim();
|
|
13154
|
+
if (!tag) {
|
|
13155
|
+
throw new Error(`${methodName}() requires \`tag\`.`);
|
|
13156
|
+
}
|
|
13157
|
+
return tag;
|
|
13158
|
+
}
|
|
13159
|
+
buildManagedEnvironmentName(tag, versionId) {
|
|
13160
|
+
return `__sdk__${tag}__${versionId}`;
|
|
13161
|
+
}
|
|
13162
|
+
matchesTagTrackedEnvironment(environment, tagName, tagId) {
|
|
13163
|
+
const environmentTagName = environment.tag?.name || environment.buildPolicy.tagName || null;
|
|
13164
|
+
return environment.tagId === tagId || environmentTagName === tagName || environment.environment === tagName || environment.envName === tagName || environment.environment === this.buildManagedEnvironmentName(tagName, environment.versionId) || environment.envName === this.buildManagedEnvironmentName(tagName, environment.versionId);
|
|
13165
|
+
}
|
|
13166
|
+
sortEnvironmentsByRecency(environments) {
|
|
13167
|
+
return [...environments].sort(
|
|
13168
|
+
(left, right) => right.updatedAt - left.updatedAt
|
|
13169
|
+
);
|
|
13170
|
+
}
|
|
13171
|
+
async resolveOpenEnvironmentData(options, methodName) {
|
|
12956
13172
|
const ontology = options.ontology;
|
|
12957
13173
|
if (!ontology) {
|
|
12958
|
-
throw new Error(
|
|
13174
|
+
throw new Error(`${methodName}() requires \`ontology\`.`);
|
|
12959
13175
|
}
|
|
12960
|
-
const
|
|
12961
|
-
if (!
|
|
12962
|
-
throw new Error(
|
|
13176
|
+
const tagName = options.tag?.trim();
|
|
13177
|
+
if (!tagName) {
|
|
13178
|
+
throw new Error(`${methodName}() requires \`tag\`.`);
|
|
12963
13179
|
}
|
|
12964
|
-
const tagName = options.tagName?.trim() || void 0;
|
|
12965
13180
|
const user = await this.resolveConnectUser(options);
|
|
13181
|
+
if (!Array.isArray(user.permissions) || user.permissions.length === 0) {
|
|
13182
|
+
throw new Error(
|
|
13183
|
+
`${methodName}() requires at least one permission so the SDK can ensure assignments for new users.`
|
|
13184
|
+
);
|
|
13185
|
+
}
|
|
12966
13186
|
const sandbox = await this.findOrCreateSandbox(ontology);
|
|
12967
13187
|
for (const profileName of user.permissions) {
|
|
12968
13188
|
const profileId = await this.ensurePermissionProfile(
|
|
@@ -12975,22 +13195,49 @@ var Granular = class _Granular {
|
|
|
12975
13195
|
profileId
|
|
12976
13196
|
);
|
|
12977
13197
|
}
|
|
12978
|
-
const
|
|
13198
|
+
const tags = await this.request(
|
|
13199
|
+
`/control/sandboxes/${sandbox.sandboxId}/tags`
|
|
13200
|
+
);
|
|
13201
|
+
const tag = (tags.items || []).find(
|
|
13202
|
+
(candidate) => Boolean(candidate?.name === tagName)
|
|
13203
|
+
);
|
|
13204
|
+
if (!tag) {
|
|
13205
|
+
throw new Error(
|
|
13206
|
+
`Tag "${tagName}" was not found for ontology ${sandbox.sandboxId}.`
|
|
13207
|
+
);
|
|
13208
|
+
}
|
|
13209
|
+
const targetVersionId = tag.targetVersionId || tag.targetBuildId;
|
|
13210
|
+
if (!targetVersionId) {
|
|
13211
|
+
throw new Error(
|
|
13212
|
+
`Tag "${tagName}" does not currently point to a build/version.`
|
|
13213
|
+
);
|
|
13214
|
+
}
|
|
13215
|
+
const allEnvironments = await this.environments.list(sandbox.sandboxId);
|
|
13216
|
+
const userEnvironments = allEnvironments.filter(
|
|
13217
|
+
(environment) => environment.subjectId === user.granularId
|
|
13218
|
+
);
|
|
13219
|
+
const currentMatches = this.sortEnvironmentsByRecency(
|
|
13220
|
+
userEnvironments.filter(
|
|
13221
|
+
(environment) => this.matchesTagTrackedEnvironment(environment, tagName, tag.tagId) && environment.versionId === targetVersionId
|
|
13222
|
+
)
|
|
13223
|
+
);
|
|
13224
|
+
if (currentMatches.length > 0) {
|
|
13225
|
+
return currentMatches[0];
|
|
13226
|
+
}
|
|
13227
|
+
const outdatedMatches = this.sortEnvironmentsByRecency(
|
|
13228
|
+
userEnvironments.filter(
|
|
13229
|
+
(environment) => this.matchesTagTrackedEnvironment(environment, tagName, tag.tagId)
|
|
13230
|
+
)
|
|
13231
|
+
);
|
|
13232
|
+
if (outdatedMatches.length > 0 && options.createFreshIfOutdated !== true) {
|
|
13233
|
+
return outdatedMatches[0];
|
|
13234
|
+
}
|
|
13235
|
+
return this.environments.create(sandbox.sandboxId, {
|
|
12979
13236
|
subjectId: user.granularId,
|
|
12980
|
-
environment:
|
|
12981
|
-
|
|
13237
|
+
environment: this.buildManagedEnvironmentName(tagName, targetVersionId),
|
|
13238
|
+
tagId: tag.tagId,
|
|
12982
13239
|
permissionProfileId: null
|
|
12983
13240
|
});
|
|
12984
|
-
await this.activateEnvironment(envData.environmentId);
|
|
12985
|
-
const session = await this.request("/ws/sessions", {
|
|
12986
|
-
method: "POST",
|
|
12987
|
-
body: JSON.stringify({
|
|
12988
|
-
environmentId: envData.environmentId,
|
|
12989
|
-
clientId,
|
|
12990
|
-
initialHeap: options.initialHeap
|
|
12991
|
-
})
|
|
12992
|
-
});
|
|
12993
|
-
return this.bindWebSocketEnvironment(envData, clientId, session);
|
|
12994
13241
|
}
|
|
12995
13242
|
/**
|
|
12996
13243
|
* List active (open) sessions for an environment — each session is one agent conversation thread.
|
|
@@ -13053,6 +13300,7 @@ var Granular = class _Granular {
|
|
|
13053
13300
|
const clientId = options.clientId || `client_${Date.now()}`;
|
|
13054
13301
|
await this.activateEnvironment(options.environmentId);
|
|
13055
13302
|
const envData = await this.environments.get(options.environmentId);
|
|
13303
|
+
const environment = this.bindEnvironmentHandle(envData);
|
|
13056
13304
|
const session = await this.request("/ws/sessions", {
|
|
13057
13305
|
method: "POST",
|
|
13058
13306
|
body: JSON.stringify({
|
|
@@ -13061,7 +13309,7 @@ var Granular = class _Granular {
|
|
|
13061
13309
|
initialHeap: options.initialHeap
|
|
13062
13310
|
})
|
|
13063
13311
|
});
|
|
13064
|
-
return this.
|
|
13312
|
+
return this.bindWebSocketEnvironmentSession(environment, clientId, session);
|
|
13065
13313
|
}
|
|
13066
13314
|
/**
|
|
13067
13315
|
* Connect to an existing open session (same conversation thread) using a freshly minted WebSocket token.
|
|
@@ -13073,7 +13321,8 @@ var Granular = class _Granular {
|
|
|
13073
13321
|
body: JSON.stringify({})
|
|
13074
13322
|
});
|
|
13075
13323
|
const envData = await this.environments.get(minted.environmentId);
|
|
13076
|
-
|
|
13324
|
+
const environment = this.bindEnvironmentHandle(envData);
|
|
13325
|
+
return this.bindWebSocketEnvironmentSession(environment, clientId, minted);
|
|
13077
13326
|
}
|
|
13078
13327
|
/**
|
|
13079
13328
|
* Mark a session closed in the control plane. If `environment` is the connected handle for that
|
|
@@ -13105,7 +13354,11 @@ var Granular = class _Granular {
|
|
|
13105
13354
|
});
|
|
13106
13355
|
return this.connectSession({ sessionId, clientId: options?.clientId });
|
|
13107
13356
|
}
|
|
13108
|
-
|
|
13357
|
+
bindEnvironmentHandle(envData) {
|
|
13358
|
+
const graphqlEndpoint = `${this.httpUrl}/orchestrator/graphql`;
|
|
13359
|
+
return new Environment(this, envData, this.apiKey, graphqlEndpoint);
|
|
13360
|
+
}
|
|
13361
|
+
async bindWebSocketEnvironmentSession(environment, clientId, session) {
|
|
13109
13362
|
const client = new WSClient({
|
|
13110
13363
|
url: session.wsUrl,
|
|
13111
13364
|
sessionId: session.sessionId,
|
|
@@ -13116,16 +13369,13 @@ var Granular = class _Granular {
|
|
|
13116
13369
|
onReconnectError: this.onReconnectError
|
|
13117
13370
|
});
|
|
13118
13371
|
await client.connect();
|
|
13119
|
-
const
|
|
13120
|
-
const environment = new Environment(
|
|
13372
|
+
const environmentSession = new EnvironmentSession(
|
|
13121
13373
|
client,
|
|
13122
|
-
|
|
13123
|
-
clientId
|
|
13124
|
-
this.apiKey,
|
|
13125
|
-
graphqlEndpoint
|
|
13374
|
+
environment,
|
|
13375
|
+
clientId
|
|
13126
13376
|
);
|
|
13127
|
-
await
|
|
13128
|
-
return
|
|
13377
|
+
await environmentSession.hello();
|
|
13378
|
+
return environmentSession;
|
|
13129
13379
|
}
|
|
13130
13380
|
async activateEnvironment(environmentId) {
|
|
13131
13381
|
await this.request(`/orchestrator/runtime/environments/${environmentId}/activate`, {
|
|
@@ -15516,6 +15766,6 @@ function buildSessionTranscript(input) {
|
|
|
15516
15766
|
});
|
|
15517
15767
|
}
|
|
15518
15768
|
|
|
15519
|
-
export { Environment, Granular, Session, WSClient, buildContinuationInstruction, buildGranularAgentCheckpointBlock, buildGranularAgentDomainBlock, buildGranularAgentHeapBlock, buildGranularAgentLoopBlock, buildGranularAgentReferentBlock, buildGranularAgentSessionBlock, buildGranularAgentSystemPrompt, buildGranularAgentToolBlock, buildGranularAgentWorkflowBlock, buildSessionTranscript, createHarnessVerifierSnapshot, evaluateContinuation, extractPromptTokens, getCurrentClosureId, getExclusivePromptTarget, hasOpenPrompt, invokeRegisteredEffect, isLocalApiUrl, normalizeEffectBehaviors, normalizePrompt, normalizePromptText, normalizePromptType, projectConversationReferentFocus, projectConversationReferentSummary, projectHeapSummary, projectLoopSummary, projectWorkflowFocus, projectWorkflowSummary, resolveApiUrl, resolveAuthTokenForApiUrl, resolveJobPresentation, resolvePromptAnswer, reviewGeneratedJobCode, scorePromptChoiceMatch };
|
|
15769
|
+
export { Environment, EnvironmentSession, Granular, OntologyHandle, Session, WSClient, buildContinuationInstruction, buildGranularAgentCheckpointBlock, buildGranularAgentDomainBlock, buildGranularAgentHeapBlock, buildGranularAgentLoopBlock, buildGranularAgentReferentBlock, buildGranularAgentSessionBlock, buildGranularAgentSystemPrompt, buildGranularAgentToolBlock, buildGranularAgentWorkflowBlock, buildSessionTranscript, createHarnessVerifierSnapshot, evaluateContinuation, extractPromptTokens, getCurrentClosureId, getExclusivePromptTarget, hasOpenPrompt, invokeRegisteredEffect, isLocalApiUrl, normalizeEffectBehaviors, normalizePrompt, normalizePromptText, normalizePromptType, projectConversationReferentFocus, projectConversationReferentSummary, projectHeapSummary, projectLoopSummary, projectWorkflowFocus, projectWorkflowSummary, resolveApiUrl, resolveAuthTokenForApiUrl, resolveJobPresentation, resolvePromptAnswer, reviewGeneratedJobCode, scorePromptChoiceMatch };
|
|
15520
15770
|
//# sourceMappingURL=index.mjs.map
|
|
15521
15771
|
//# sourceMappingURL=index.mjs.map
|