@granular-software/sdk 0.4.13 → 0.4.15

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.mjs CHANGED
@@ -5591,6 +5591,19 @@ function normalizeUser(user) {
5591
5591
  permissions: Array.isArray(user.permissions) ? user.permissions : []
5592
5592
  };
5593
5593
  }
5594
+ function normalizeEnvironmentData(environment) {
5595
+ const buildPolicy = environment.buildPolicy || environment.tracking || (environment.tagId ? { mode: "tag", tagId: environment.tagId } : { mode: "pinned", versionId: environment.versionId || environment.buildId });
5596
+ const environmentName = environment.environment || environment.envName || "prod";
5597
+ return {
5598
+ ...environment,
5599
+ ontologyId: environment.ontologyId || environment.sandboxId,
5600
+ versionId: environment.versionId || environment.buildId,
5601
+ envName: environmentName,
5602
+ environment: environmentName,
5603
+ buildPolicy,
5604
+ tracking: environment.tracking || buildPolicy
5605
+ };
5606
+ }
5594
5607
  var Environment = class extends Session {
5595
5608
  envData;
5596
5609
  _apiKey;
@@ -5609,10 +5622,26 @@ var Environment = class extends Session {
5609
5622
  get sandboxId() {
5610
5623
  return this.envData.sandboxId;
5611
5624
  }
5625
+ /** The ontology ID */
5626
+ get ontologyId() {
5627
+ return this.envData.ontologyId || this.envData.sandboxId;
5628
+ }
5612
5629
  /** The subject ID */
5613
5630
  get subjectId() {
5614
5631
  return this.envData.subjectId;
5615
5632
  }
5633
+ /** The named environment slot, such as dev or prod */
5634
+ get envName() {
5635
+ return this.envData.envName;
5636
+ }
5637
+ /** The named environment slot, such as dev or prod */
5638
+ get environment() {
5639
+ return this.envData.environment || this.envData.envName;
5640
+ }
5641
+ /** The resolved ontology version backing this environment */
5642
+ get versionId() {
5643
+ return this.envData.versionId || this.envData.buildId;
5644
+ }
5616
5645
  /** Internal Granular user identifier for this environment */
5617
5646
  get granularId() {
5618
5647
  return this.envData.subjectId;
@@ -5696,7 +5725,8 @@ var Environment = class extends Session {
5696
5725
  "Authorization": `Bearer ${this._apiKey}`
5697
5726
  },
5698
5727
  body: JSON.stringify({
5699
- reason: "sdk_disconnect_http_fallback"
5728
+ reason: "sdk_disconnect_http_fallback",
5729
+ sessionId: this.client.currentSessionId
5700
5730
  })
5701
5731
  }
5702
5732
  );
@@ -6480,7 +6510,7 @@ var Environment = class extends Session {
6480
6510
  return super.unpublishAllEffects();
6481
6511
  }
6482
6512
  };
6483
- var Granular = class {
6513
+ var Granular = class _Granular {
6484
6514
  apiKey;
6485
6515
  apiUrl;
6486
6516
  httpUrl;
@@ -6584,7 +6614,7 @@ var Granular = class {
6584
6614
  throw new Error("connect() requires either userId, granularId, or a user object returned by recordUser().");
6585
6615
  }
6586
6616
  /**
6587
- * Connect to a sandbox and establish a real-time environment session.
6617
+ * Connect to an ontology environment and establish a real-time session.
6588
6618
  *
6589
6619
  * Effects are registered at the sandbox level via `granular.registerEffect()`
6590
6620
  * or `granular.registerEffects()`. Sessions pick up live availability from
@@ -6596,7 +6626,8 @@ var Granular = class {
6596
6626
  * @example
6597
6627
  * ```typescript
6598
6628
  * const environment = await granular.connect({
6599
- * sandbox: 'my-sandbox',
6629
+ * ontology: 'my-ontology',
6630
+ * environment: 'dev',
6600
6631
  * userId: 'user_123',
6601
6632
  * permissions: ['agent'],
6602
6633
  * });
@@ -6616,17 +6647,28 @@ var Granular = class {
6616
6647
  *
6617
6648
  * console.log(await job.result); // 'Hello!'
6618
6649
  * ```
6619
- */
6650
+ */
6620
6651
  async connect(options) {
6621
6652
  const clientId = options.clientId || `client_${Date.now()}`;
6653
+ const ontology = options.ontology;
6654
+ if (!ontology) {
6655
+ throw new Error("connect() requires `ontology`.");
6656
+ }
6657
+ const environmentName = options.environment;
6658
+ if (!environmentName) {
6659
+ throw new Error("connect() requires `environment`.");
6660
+ }
6661
+ const tagName = options.tagName?.trim() || void 0;
6622
6662
  const user = await this.resolveConnectUser(options);
6623
- const sandbox = await this.findOrCreateSandbox(options.sandbox);
6663
+ const sandbox = await this.findOrCreateSandbox(ontology);
6624
6664
  for (const profileName of user.permissions) {
6625
6665
  const profileId = await this.ensurePermissionProfile(sandbox.sandboxId, profileName);
6626
6666
  await this.ensureAssignment(user.granularId, sandbox.sandboxId, profileId);
6627
6667
  }
6628
6668
  const envData = await this.environments.create(sandbox.sandboxId, {
6629
6669
  subjectId: user.granularId,
6670
+ environment: environmentName,
6671
+ tagName,
6630
6672
  permissionProfileId: null
6631
6673
  });
6632
6674
  await this.activateEnvironment(envData.environmentId);
@@ -6638,6 +6680,120 @@ var Granular = class {
6638
6680
  initialHeap: options.initialHeap
6639
6681
  })
6640
6682
  });
6683
+ return this.bindWebSocketEnvironment(envData, clientId, session);
6684
+ }
6685
+ /**
6686
+ * List active (open) sessions for an environment — each session is one agent conversation thread.
6687
+ */
6688
+ async listOpenSessions(filters) {
6689
+ return this.listSessionsForEnvironment(filters.environmentId, "active");
6690
+ }
6691
+ /**
6692
+ * List closed sessions for an environment (conversations that have disconnected).
6693
+ */
6694
+ async listClosedSessions(filters) {
6695
+ return this.listSessionsForEnvironment(filters.environmentId, "closed");
6696
+ }
6697
+ async listSessionsForEnvironment(environmentId, status) {
6698
+ const query = new URLSearchParams({ environmentId, status });
6699
+ const res = await this.request(
6700
+ `/control/sessions?${query.toString()}`
6701
+ );
6702
+ const items = Array.isArray(res.items) ? res.items : [];
6703
+ return items.map((row) => this.normalizeConversationSession(row));
6704
+ }
6705
+ normalizeConversationSession(row) {
6706
+ const sessionId = String(row.sessionId ?? row.session_id ?? "");
6707
+ const environmentId = String(row.environmentId ?? row.environment_id ?? "");
6708
+ return {
6709
+ sessionId,
6710
+ tenantId: row.tenantId != null ? String(row.tenantId) : void 0,
6711
+ environmentId,
6712
+ versionId: row.versionId != null ? String(row.versionId) : null,
6713
+ docId: String(row.docId ?? row.doc_id ?? ""),
6714
+ status: row.status || "active",
6715
+ createdAt: _Granular.coerceIsoDate(row.createdAt ?? row.created_at),
6716
+ lastSeenAt: _Granular.coerceIsoDate(row.lastSeenAt ?? row.last_seen_at),
6717
+ summary: row.summary != null ? String(row.summary) : null,
6718
+ summaryUpdatedAt: row.summaryUpdatedAt != null || row.summary_updated_at != null ? _Granular.coerceIsoDate(row.summaryUpdatedAt ?? row.summary_updated_at) : null,
6719
+ subjectId: row.subjectId != null ? String(row.subjectId) : null,
6720
+ jobCount: typeof row.jobCount === "number" ? row.jobCount : void 0,
6721
+ toolCallCount: typeof row.toolCallCount === "number" ? row.toolCallCount : void 0
6722
+ };
6723
+ }
6724
+ static coerceIsoDate(value) {
6725
+ if (value instanceof Date) {
6726
+ return value.toISOString();
6727
+ }
6728
+ if (typeof value === "number") {
6729
+ return new Date(value).toISOString();
6730
+ }
6731
+ if (typeof value === "string") {
6732
+ return value;
6733
+ }
6734
+ return (/* @__PURE__ */ new Date(0)).toISOString();
6735
+ }
6736
+ /**
6737
+ * Create a new session (conversation) for an existing environment and connect to it.
6738
+ * The runtime graph is shared across all sessions for the same environment.
6739
+ */
6740
+ async createSession(options) {
6741
+ const clientId = options.clientId || `client_${Date.now()}`;
6742
+ await this.activateEnvironment(options.environmentId);
6743
+ const envData = await this.environments.get(options.environmentId);
6744
+ const session = await this.request("/ws/sessions", {
6745
+ method: "POST",
6746
+ body: JSON.stringify({
6747
+ environmentId: options.environmentId,
6748
+ clientId,
6749
+ initialHeap: options.initialHeap
6750
+ })
6751
+ });
6752
+ return this.bindWebSocketEnvironment(envData, clientId, session);
6753
+ }
6754
+ /**
6755
+ * Connect to an existing open session (same conversation thread) using a freshly minted WebSocket token.
6756
+ */
6757
+ async connectSession(options) {
6758
+ const clientId = options.clientId || `client_${Date.now()}`;
6759
+ const minted = await this.request(`/ws/sessions/${encodeURIComponent(options.sessionId)}/token`, {
6760
+ method: "POST",
6761
+ body: JSON.stringify({})
6762
+ });
6763
+ const envData = await this.environments.get(minted.environmentId);
6764
+ return this.bindWebSocketEnvironment(envData, clientId, minted);
6765
+ }
6766
+ /**
6767
+ * Mark a session closed in the control plane. If `environment` is the connected handle for that
6768
+ * `sessionId`, disconnects the WebSocket so the runtime tears down cleanly.
6769
+ */
6770
+ async closeSession(sessionId, environment) {
6771
+ if (environment && environment.sessionId === sessionId) {
6772
+ await environment.disconnect();
6773
+ return;
6774
+ }
6775
+ await this.request(`/control/sessions/${encodeURIComponent(sessionId)}`, {
6776
+ method: "PATCH",
6777
+ body: JSON.stringify({
6778
+ status: "closed",
6779
+ lastSeenAt: (/* @__PURE__ */ new Date()).toISOString()
6780
+ })
6781
+ });
6782
+ }
6783
+ /**
6784
+ * Re-open a closed session in the index and connect to its existing runtime document.
6785
+ */
6786
+ async reopenSession(sessionId, options) {
6787
+ await this.request(`/control/sessions/${encodeURIComponent(sessionId)}`, {
6788
+ method: "PATCH",
6789
+ body: JSON.stringify({
6790
+ status: "active",
6791
+ lastSeenAt: (/* @__PURE__ */ new Date()).toISOString()
6792
+ })
6793
+ });
6794
+ return this.connectSession({ sessionId, clientId: options?.clientId });
6795
+ }
6796
+ async bindWebSocketEnvironment(envData, clientId, session) {
6641
6797
  const client = new WSClient({
6642
6798
  url: session.wsUrl,
6643
6799
  sessionId: session.sessionId,
@@ -7061,16 +7217,22 @@ var Granular = class {
7061
7217
  const result = await this.request(
7062
7218
  `/control/sandboxes/${sandboxId}/environments`
7063
7219
  );
7064
- return result.items;
7220
+ return result.items.map(normalizeEnvironmentData);
7065
7221
  },
7066
7222
  get: async (environmentId) => {
7067
- return this.request(`/control/environments/${environmentId}`);
7223
+ return normalizeEnvironmentData(
7224
+ await this.request(`/control/environments/${environmentId}`)
7225
+ );
7068
7226
  },
7069
7227
  create: async (sandboxId, data) => {
7070
- return this.request(`/control/sandboxes/${sandboxId}/environments`, {
7228
+ const environmentName = data.environment || data.envName;
7229
+ return normalizeEnvironmentData(await this.request(`/control/sandboxes/${sandboxId}/environments`, {
7071
7230
  method: "POST",
7072
- body: JSON.stringify(data)
7073
- });
7231
+ body: JSON.stringify({
7232
+ ...data,
7233
+ envName: environmentName
7234
+ })
7235
+ }));
7074
7236
  },
7075
7237
  delete: async (environmentId) => {
7076
7238
  return this.request(`/control/environments/${environmentId}`, {