@granular-software/sdk 0.4.28 → 0.4.30

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/cli/index.js CHANGED
@@ -14230,17 +14230,8 @@ var searchableMetamodelPackage = defineMetamodelPackage({
14230
14230
  }
14231
14231
  },
14232
14232
  domain: {
14233
- applyToPropertyIR(propertyIR, propertySummary) {
14234
- if (String(propertySummary.type || "").toLowerCase() !== "string" || propertySummary.searchable === false) {
14235
- return propertyIR;
14236
- }
14237
- return {
14238
- ...propertyIR,
14239
- docs: [
14240
- ...propertyIR.docs,
14241
- propertySummary.searchablePhonetic ? "Searchable via `search` using FalkorDB full-text query syntax with phonetic matching enabled." : "Searchable via `search` using FalkorDB full-text query syntax."
14242
- ]
14243
- };
14233
+ applyToPropertyIR(propertyIR, _propertySummary) {
14234
+ return propertyIR;
14244
14235
  }
14245
14236
  }
14246
14237
  });
@@ -15038,6 +15029,8 @@ Runtime note: class-wide \`search\` is implemented as one denormalized full-text
15038
15029
  Preferred search ladder: start with plain text such as \`find({ search: "example name" })\`. If results are empty or ambiguous, retry with RediSearch-style fuzzy / prefix / OR operators. Add \`filter\` only when you need exact structural narrowing.
15039
15030
  Plain string \`search\` is normalized for human text before it reaches full-text search, so case, punctuation, repeated whitespace, and diacritics do not need to be manually enumerated in agent code.
15040
15031
  Do not manually enumerate capitalization / punctuation / spacing variants in agent code. Use plain \`search\` first, then escalate to fuzzy / prefix / OR operators only if the first pass is empty or ambiguous.
15032
+ Sorting is first-class too: use generated runtime syntax like \`page({ perPage: 10, sort: { field: "year", order: "asc" } })\` or GraphQL syntax like \`direct_instances(per_page: 10, sort: { at: "year", order: ASC })\` when result order matters.
15033
+ Sorting is applied in FalkorDB before pagination and limit selection. Do not load a page and sort it afterward in agent code.
15041
15034
  Optional indexing note: \`"searchable": { "phonetic": true }\` keeps the field searchable and enables FalkorDB phonetic matching for the class-wide search index. Query syntax does not change; agents should keep using normal \`search\` strings.
15042
15035
  Without an explicit sort, indexed \`search\` results are returned in FalkorDB relevance order. If you pass a sort, that explicit sort takes precedence.
15043
15036
 
@@ -19518,17 +19511,40 @@ function buildEffectMetamodelMutations(toolPath, spec) {
19518
19511
 
19519
19512
  // src/client.ts
19520
19513
  var STANDARD_MODULES_OPERATIONS = [
19521
- { create: "entity", has: { id: { value: "auto-generated" }, createdAt: { value: void 0 } } },
19514
+ {
19515
+ create: "entity",
19516
+ has: { id: { value: "auto-generated" }, createdAt: { value: void 0 } }
19517
+ },
19522
19518
  { create: "class", extends: "entity", has: {} },
19523
- { create: "user", extends: "entity", has: { email: { value: void 0 }, firstName: { value: void 0 }, lastName: { value: void 0 } } },
19524
- { create: "company", extends: "entity", has: { name: { value: void 0 }, website: { value: void 0 } } },
19519
+ {
19520
+ create: "user",
19521
+ extends: "entity",
19522
+ has: {
19523
+ email: { value: void 0 },
19524
+ firstName: { value: void 0 },
19525
+ lastName: { value: void 0 }
19526
+ }
19527
+ },
19528
+ {
19529
+ create: "company",
19530
+ extends: "entity",
19531
+ has: { name: { value: void 0 }, website: { value: void 0 } }
19532
+ },
19525
19533
  { create: "string", has: {} },
19526
19534
  { create: "number", has: {} },
19527
19535
  { create: "boolean", has: {} },
19528
- { create: "tool_parameter", has: { name: { value: void 0 }, type: { value: "string" }, description: { value: void 0 }, required: { value: false } } }
19536
+ {
19537
+ create: "tool_parameter",
19538
+ has: {
19539
+ name: { value: void 0 },
19540
+ type: { value: "string" },
19541
+ description: { value: void 0 },
19542
+ required: { value: false }
19543
+ }
19544
+ }
19529
19545
  ];
19530
19546
  var BUILTIN_MODULES = {
19531
- "standard_modules": STANDARD_MODULES_OPERATIONS
19547
+ standard_modules: STANDARD_MODULES_OPERATIONS
19532
19548
  };
19533
19549
  var DEFAULT_DIRECT_RECORD_OBJECTS_REQUEST_BATCH_SIZE = 100;
19534
19550
  var MAX_RECORD_OBJECTS_CONCURRENCY = 16;
@@ -19563,7 +19579,9 @@ function isRetryableLocalWorkerRestart(status, body, url) {
19563
19579
  }
19564
19580
  function isRetryableRecordObjectsError(error2) {
19565
19581
  const message = error2 instanceof Error ? error2.message : String(error2);
19566
- return /worker restarted mid-request|econnreset|network|socket connection was closed unexpectedly|timed out/i.test(message);
19582
+ return /worker restarted mid-request|econnreset|network|socket connection was closed unexpectedly|timed out/i.test(
19583
+ message
19584
+ );
19567
19585
  }
19568
19586
  function computeEffectKey2(effect) {
19569
19587
  const attachedClass = effect.className?.trim();
@@ -19640,7 +19658,10 @@ function normalizeUser(user) {
19640
19658
  };
19641
19659
  }
19642
19660
  function normalizeEnvironmentData(environment) {
19643
- const buildPolicy = environment.buildPolicy || environment.tracking || (environment.tagId ? { mode: "tag", tagId: environment.tagId } : { mode: "pinned", versionId: environment.versionId || environment.buildId });
19661
+ const buildPolicy = environment.buildPolicy || environment.tracking || (environment.tagId ? { mode: "tag", tagId: environment.tagId } : {
19662
+ mode: "pinned",
19663
+ versionId: environment.versionId || environment.buildId
19664
+ });
19644
19665
  const environmentName = environment.environment || environment.envName || "prod";
19645
19666
  return {
19646
19667
  ...environment,
@@ -19734,14 +19755,16 @@ var Environment = class extends Session {
19734
19755
  const response = await fetch(url, {
19735
19756
  ...options,
19736
19757
  headers: {
19737
- "Authorization": `Bearer ${this._apiKey}`,
19758
+ Authorization: `Bearer ${this._apiKey}`,
19738
19759
  "Content-Type": "application/json",
19739
- "Connection": "close",
19760
+ Connection: "close",
19740
19761
  ...options.headers
19741
19762
  }
19742
19763
  });
19743
19764
  if (!response.ok) {
19744
- throw new Error(`Control Plane API Error (${response.status}): ${await response.text()}`);
19765
+ throw new Error(
19766
+ `Control Plane API Error (${response.status}): ${await response.text()}`
19767
+ );
19745
19768
  }
19746
19769
  return response.json();
19747
19770
  }
@@ -19755,9 +19778,12 @@ var Environment = class extends Session {
19755
19778
  async disconnect() {
19756
19779
  let wsNotifiedRuntime = false;
19757
19780
  try {
19758
- const goodbye = await this.rpc("client.goodbye", {
19759
- timestamp: Date.now()
19760
- });
19781
+ const goodbye = await this.rpc(
19782
+ "client.goodbye",
19783
+ {
19784
+ timestamp: Date.now()
19785
+ }
19786
+ );
19761
19787
  wsNotifiedRuntime = Boolean(goodbye?.ok && goodbye?.via);
19762
19788
  } catch {
19763
19789
  wsNotifiedRuntime = false;
@@ -19771,8 +19797,8 @@ var Environment = class extends Session {
19771
19797
  method: "POST",
19772
19798
  headers: {
19773
19799
  "Content-Type": "application/json",
19774
- "Authorization": `Bearer ${this._apiKey}`,
19775
- "Connection": "close"
19800
+ Authorization: `Bearer ${this._apiKey}`,
19801
+ Connection: "close"
19776
19802
  },
19777
19803
  body: JSON.stringify({
19778
19804
  reason: "sdk_disconnect_http_fallback",
@@ -19785,6 +19811,15 @@ var Environment = class extends Session {
19785
19811
  }
19786
19812
  this.client.disconnect();
19787
19813
  }
19814
+ /**
19815
+ * Close only the socket transport without sending `client.goodbye`.
19816
+ *
19817
+ * Use this when the caller intends to immediately reattach to the same
19818
+ * session after an unexpected disconnect.
19819
+ */
19820
+ disconnectTransport() {
19821
+ this.client.disconnect();
19822
+ }
19788
19823
  // ==================== GRAPH CONTAINER READINESS ====================
19789
19824
  /** The last known graph container status, updated by checkReadiness() or on heartbeat */
19790
19825
  graphContainerStatus = null;
@@ -19850,14 +19885,14 @@ var Environment = class extends Session {
19850
19885
  }
19851
19886
  /**
19852
19887
  * Execute a GraphQL query against the environment's graph.
19853
- *
19888
+ *
19854
19889
  * The query uses the Granular graph query language (based on Cypher/GraphQL).
19855
19890
  * Authentication is handled automatically using the SDK's API key.
19856
- *
19891
+ *
19857
19892
  * @param query - The GraphQL query string
19858
19893
  * @param variables - Optional variables for the query
19859
19894
  * @returns The query result data
19860
- *
19895
+ *
19861
19896
  * @example
19862
19897
  * ```typescript
19863
19898
  * // Read the workspace
@@ -19865,7 +19900,7 @@ var Environment = class extends Session {
19865
19900
  * `query { model(path: "workspace") { path label submodels { path label } } }`
19866
19901
  * );
19867
19902
  * console.log(result.data);
19868
- *
19903
+ *
19869
19904
  * // Create a model
19870
19905
  * const created = await env.graphql(
19871
19906
  * `mutation { at(path: "workspace") { create_submodel(subpath: "my_node", label: "My Node", prototype: "Model") { model { path label } } } }`
@@ -19877,7 +19912,7 @@ var Environment = class extends Session {
19877
19912
  method: "POST",
19878
19913
  headers: {
19879
19914
  "Content-Type": "application/json",
19880
- "Authorization": `Bearer ${this._apiKey}`
19915
+ Authorization: `Bearer ${this._apiKey}`
19881
19916
  },
19882
19917
  body: JSON.stringify({
19883
19918
  environmentId: this.environmentId,
@@ -19894,10 +19929,10 @@ var Environment = class extends Session {
19894
19929
  // ==================== RELATIONSHIP METHODS ====================
19895
19930
  /**
19896
19931
  * Define a relationship between two model types.
19897
- *
19932
+ *
19898
19933
  * Creates both submodels (if they don't exist) and links them with
19899
19934
  * a RelationshipDef node that encodes cardinality.
19900
- *
19935
+ *
19901
19936
  * @example
19902
19937
  * ```typescript
19903
19938
  * // Author has many Books, Book has one Author
@@ -19957,10 +19992,10 @@ var Environment = class extends Session {
19957
19992
  }
19958
19993
  /**
19959
19994
  * Get all relationships for a model type.
19960
- *
19995
+ *
19961
19996
  * @param modelPath - The model type path (e.g., "author")
19962
19997
  * @returns Array of relationships from this model's perspective
19963
- *
19998
+ *
19964
19999
  * @example
19965
20000
  * ```typescript
19966
20001
  * const rels = await env.getRelationships('author');
@@ -19993,18 +20028,18 @@ var Environment = class extends Session {
19993
20028
  }
19994
20029
  /**
19995
20030
  * Attach a target model to a relationship submodel.
19996
- *
20031
+ *
19997
20032
  * Handles cardinality automatically:
19998
20033
  * - "One" side: sets/replaces the reference
19999
20034
  * - "Many" side: adds the target to the collection
20000
- *
20035
+ *
20001
20036
  * If the target model doesn't exist, it's created as an instance of the foreign type.
20002
20037
  * Bidirectional sync is automatic.
20003
- *
20038
+ *
20004
20039
  * @param modelPath - The model instance path (e.g., "tolkien")
20005
20040
  * @param submodelPath - The relationship submodel (e.g., "books")
20006
20041
  * @param targetPath - The target model to attach (e.g., "lord_of_the_rings")
20007
- *
20042
+ *
20008
20043
  * @example
20009
20044
  * ```typescript
20010
20045
  * // Attach a book to an author (many side)
@@ -20031,18 +20066,18 @@ var Environment = class extends Session {
20031
20066
  }
20032
20067
  /**
20033
20068
  * Detach a target model from a relationship submodel.
20034
- *
20069
+ *
20035
20070
  * Handles bidirectional cleanup automatically.
20036
- *
20071
+ *
20037
20072
  * @param modelPath - The model instance path
20038
20073
  * @param submodelPath - The relationship submodel
20039
20074
  * @param targetPath - The target to detach (optional for "one" side; omit on "many" side to detach all)
20040
- *
20075
+ *
20041
20076
  * @example
20042
20077
  * ```typescript
20043
20078
  * // Detach a specific book
20044
20079
  * await env.detach('tolkien', 'books', 'lord_of_the_rings');
20045
- *
20080
+ *
20046
20081
  * // Detach all books
20047
20082
  * await env.detach('tolkien', 'books');
20048
20083
  * ```
@@ -20066,11 +20101,11 @@ var Environment = class extends Session {
20066
20101
  }
20067
20102
  /**
20068
20103
  * List all related models through a relationship submodel.
20069
- *
20104
+ *
20070
20105
  * @param modelPath - The model instance path
20071
20106
  * @param submodelPath - The relationship submodel
20072
20107
  * @returns Array of related model references
20073
- *
20108
+ *
20074
20109
  * @example
20075
20110
  * ```typescript
20076
20111
  * const books = await env.listRelated('tolkien', 'books');
@@ -20094,14 +20129,14 @@ var Environment = class extends Session {
20094
20129
  }
20095
20130
  /**
20096
20131
  * Apply a manifest to the current environment's graph.
20097
- *
20132
+ *
20098
20133
  * Translates each manifest operation into GraphQL mutations and executes them
20099
20134
  * in order. This is the core mechanism for creating classes, fields, and
20100
20135
  * relationships from a declarative manifest.
20101
- *
20136
+ *
20102
20137
  * @param manifest - The manifest content to apply
20103
20138
  * @returns Summary of applied operations
20104
- *
20139
+ *
20105
20140
  * @example
20106
20141
  * ```typescript
20107
20142
  * await environment.applyManifest({
@@ -20139,12 +20174,16 @@ var Environment = class extends Session {
20139
20174
  applied++;
20140
20175
  } catch (err) {
20141
20176
  if (!err.message?.includes("already exists")) {
20142
- errors.push(`Import ${imp.name} operation failed: ${err.message}`);
20177
+ errors.push(
20178
+ `Import ${imp.name} operation failed: ${err.message}`
20179
+ );
20143
20180
  }
20144
20181
  }
20145
20182
  }
20146
20183
  } else {
20147
- errors.push(`Unknown module: "${imp.name}" (only built-in modules are supported)`);
20184
+ errors.push(
20185
+ `Unknown module: "${imp.name}" (only built-in modules are supported)`
20186
+ );
20148
20187
  }
20149
20188
  }
20150
20189
  }
@@ -20228,7 +20267,10 @@ var Environment = class extends Session {
20228
20267
  }
20229
20268
  }
20230
20269
  async _applyEffectMetamodels(toolPath, metamodels) {
20231
- for (const mutation of buildEffectMetamodelMutations(toolPath, metamodels)) {
20270
+ for (const mutation of buildEffectMetamodelMutations(
20271
+ toolPath,
20272
+ metamodels
20273
+ )) {
20232
20274
  await this._runGraphql(mutation.query, mutation.label);
20233
20275
  }
20234
20276
  }
@@ -20277,7 +20319,9 @@ var Environment = class extends Session {
20277
20319
  }
20278
20320
  if (eventType.payloadSchema?.properties) {
20279
20321
  const fieldSpecs = {};
20280
- for (const [propName, propSchema] of Object.entries(eventType.payloadSchema.properties)) {
20322
+ for (const [propName, propSchema] of Object.entries(
20323
+ eventType.payloadSchema.properties
20324
+ )) {
20281
20325
  const schema = propSchema;
20282
20326
  fieldSpecs[propName] = {
20283
20327
  type: schema.type ?? "string",
@@ -20560,7 +20604,9 @@ var Environment = class extends Session {
20560
20604
  const wave = plans.slice(waveStart, waveStart + concurrency);
20561
20605
  await Promise.all(
20562
20606
  wave.map(async (plan) => {
20563
- const { items, durationMs } = await this.executeRecordObjectsChunk(plan.slice);
20607
+ const { items, durationMs } = await this.executeRecordObjectsChunk(
20608
+ plan.slice
20609
+ );
20564
20610
  if (items.length !== plan.slice.length) {
20565
20611
  throw new Error(
20566
20612
  `recordObjects: chunk ${plan.chunkIndex + 1} returned ${items.length} results, expected ${plan.slice.length}`
@@ -20590,13 +20636,10 @@ var Environment = class extends Session {
20590
20636
  let lastError;
20591
20637
  for (let attempt = 1; attempt <= DEFAULT_DIRECT_RECORD_OBJECTS_RETRY_COUNT; attempt += 1) {
20592
20638
  try {
20593
- const response = await this.controlPlaneRequest(
20594
- `/control/environments/${this.environmentId}/records/batch`,
20595
- {
20596
- method: "POST",
20597
- body: JSON.stringify({ records: chunk })
20598
- }
20599
- );
20639
+ const response = await this.controlPlaneRequest(`/control/environments/${this.environmentId}/records/batch`, {
20640
+ method: "POST",
20641
+ body: JSON.stringify({ records: chunk })
20642
+ });
20600
20643
  const items = Array.isArray(response.items) ? response.items : [];
20601
20644
  return { items, durationMs: Date.now() - wallStart };
20602
20645
  } catch (error2) {
@@ -20659,7 +20702,9 @@ var Environment = class extends Session {
20659
20702
  * Fetch a single record import by id.
20660
20703
  */
20661
20704
  async getRecordImport(importId) {
20662
- return this.controlPlaneRequest(`/control/record-imports/${importId}`);
20705
+ return this.controlPlaneRequest(
20706
+ `/control/record-imports/${importId}`
20707
+ );
20663
20708
  }
20664
20709
  /**
20665
20710
  * Cancel a queued/background record import.
@@ -20726,7 +20771,9 @@ var Granular = class _Granular {
20726
20771
  constructor(options) {
20727
20772
  const auth = options.token ?? options.apiKey;
20728
20773
  if (!auth) {
20729
- throw new Error("Granular client requires either apiKey or token. Set GRANULAR_API_KEY or GRANULAR_TOKEN, or pass one in options.");
20774
+ throw new Error(
20775
+ "Granular client requires either apiKey or token. Set GRANULAR_API_KEY or GRANULAR_TOKEN, or pass one in options."
20776
+ );
20730
20777
  }
20731
20778
  this.apiUrl = resolveApiUrl(options.apiUrl, options.endpointMode);
20732
20779
  this.apiKey = resolveAuthTokenForApiUrl(auth, this.apiUrl);
@@ -20738,10 +20785,10 @@ var Granular = class _Granular {
20738
20785
  }
20739
20786
  /**
20740
20787
  * Records/upserts a user and prepares them for sandbox connections
20741
- *
20788
+ *
20742
20789
  * @param options - User options
20743
20790
  * @returns The recorded user with both `userId` and `granularId`
20744
- *
20791
+ *
20745
20792
  * @example
20746
20793
  * ```typescript
20747
20794
  * const user = await granular.recordUser({
@@ -20752,14 +20799,16 @@ var Granular = class _Granular {
20752
20799
  * ```
20753
20800
  */
20754
20801
  async recordUser(options) {
20755
- const subject = normalizeSubject(await this.request("/control/subjects", {
20756
- method: "POST",
20757
- body: JSON.stringify({
20758
- identityId: options.userId,
20759
- name: options.name,
20760
- email: options.email
20802
+ const subject = normalizeSubject(
20803
+ await this.request("/control/subjects", {
20804
+ method: "POST",
20805
+ body: JSON.stringify({
20806
+ identityId: options.userId,
20807
+ name: options.name,
20808
+ email: options.email
20809
+ })
20761
20810
  })
20762
- }));
20811
+ );
20763
20812
  return normalizeUser({
20764
20813
  granularId: subject.granularId,
20765
20814
  userId: options.userId,
@@ -20806,18 +20855,20 @@ var Granular = class _Granular {
20806
20855
  permissions: options.permissions || []
20807
20856
  };
20808
20857
  }
20809
- throw new Error("connect() requires either userId, granularId, or a user object returned by recordUser().");
20858
+ throw new Error(
20859
+ "connect() requires either userId, granularId, or a user object returned by recordUser()."
20860
+ );
20810
20861
  }
20811
20862
  /**
20812
20863
  * Connect to an ontology environment and establish a real-time session.
20813
- *
20864
+ *
20814
20865
  * Effects are registered at the sandbox level via `granular.registerEffect()`
20815
20866
  * or `granular.registerEffects()`. Sessions pick up live availability from
20816
20867
  * the sandbox registry automatically.
20817
- *
20868
+ *
20818
20869
  * @param options - Connection options
20819
20870
  * @returns An active environment session
20820
- *
20871
+ *
20821
20872
  * @example
20822
20873
  * ```typescript
20823
20874
  * const environment = await granular.connect({
@@ -20826,23 +20877,23 @@ var Granular = class _Granular {
20826
20877
  * userId: 'user_123',
20827
20878
  * permissions: ['agent'],
20828
20879
  * });
20829
- *
20880
+ *
20830
20881
  * await granular.registerEffect('my-sandbox', {
20831
20882
  * name: 'greet',
20832
20883
  * description: 'Say hello',
20833
20884
  * inputSchema: { type: 'object', properties: {} },
20834
20885
  * handler: async () => 'Hello!',
20835
20886
  * });
20836
- *
20887
+ *
20837
20888
  * // Submit job
20838
20889
  * const job = await environment.submitJob(`
20839
20890
  * import { tools } from './sandbox-tools';
20840
20891
  * return await tools.greet({});
20841
20892
  * `);
20842
- *
20893
+ *
20843
20894
  * console.log(await job.result); // 'Hello!'
20844
20895
  * ```
20845
- */
20896
+ */
20846
20897
  async connect(options) {
20847
20898
  const clientId = options.clientId || `client_${Date.now()}`;
20848
20899
  const ontology = options.ontology;
@@ -20857,8 +20908,15 @@ var Granular = class _Granular {
20857
20908
  const user = await this.resolveConnectUser(options);
20858
20909
  const sandbox = await this.findOrCreateSandbox(ontology);
20859
20910
  for (const profileName of user.permissions) {
20860
- const profileId = await this.ensurePermissionProfile(sandbox.sandboxId, profileName);
20861
- await this.ensureAssignment(user.granularId, sandbox.sandboxId, profileId);
20911
+ const profileId = await this.ensurePermissionProfile(
20912
+ sandbox.sandboxId,
20913
+ profileName
20914
+ );
20915
+ await this.ensureAssignment(
20916
+ user.granularId,
20917
+ sandbox.sandboxId,
20918
+ profileId
20919
+ );
20862
20920
  }
20863
20921
  const envData = await this.environments.create(sandbox.sandboxId, {
20864
20922
  subjectId: user.granularId,
@@ -20910,7 +20968,9 @@ var Granular = class _Granular {
20910
20968
  createdAt: _Granular.coerceIsoDate(row.createdAt ?? row.created_at),
20911
20969
  lastSeenAt: _Granular.coerceIsoDate(row.lastSeenAt ?? row.last_seen_at),
20912
20970
  summary: row.summary != null ? String(row.summary) : null,
20913
- summaryUpdatedAt: row.summaryUpdatedAt != null || row.summary_updated_at != null ? _Granular.coerceIsoDate(row.summaryUpdatedAt ?? row.summary_updated_at) : null,
20971
+ summaryUpdatedAt: row.summaryUpdatedAt != null || row.summary_updated_at != null ? _Granular.coerceIsoDate(
20972
+ row.summaryUpdatedAt ?? row.summary_updated_at
20973
+ ) : null,
20914
20974
  subjectId: row.subjectId != null ? String(row.subjectId) : null,
20915
20975
  jobCount: typeof row.jobCount === "number" ? row.jobCount : void 0,
20916
20976
  toolCallCount: typeof row.toolCallCount === "number" ? row.toolCallCount : void 0
@@ -21000,7 +21060,13 @@ var Granular = class _Granular {
21000
21060
  });
21001
21061
  await client.connect();
21002
21062
  const graphqlEndpoint = `${this.httpUrl}/orchestrator/graphql`;
21003
- const environment = new Environment(client, envData, clientId, this.apiKey, graphqlEndpoint);
21063
+ const environment = new Environment(
21064
+ client,
21065
+ envData,
21066
+ clientId,
21067
+ this.apiKey,
21068
+ graphqlEndpoint
21069
+ );
21004
21070
  await environment.hello();
21005
21071
  return environment;
21006
21072
  }
@@ -21034,14 +21100,18 @@ var Granular = class _Granular {
21034
21100
  };
21035
21101
  }
21036
21102
  async publishSandboxEffectCatalog(host) {
21037
- const effects2 = Array.from(this.getSandboxEffectMap(host.sandboxId).values()).map(
21038
- (effect) => this.serializeEffect(effect)
21039
- );
21040
- const result = await host.wsClient.call("effects.publishCatalog", { effects: effects2 });
21103
+ const effects2 = Array.from(
21104
+ this.getSandboxEffectMap(host.sandboxId).values()
21105
+ ).map((effect) => this.serializeEffect(effect));
21106
+ const result = await host.wsClient.call("effects.publishCatalog", {
21107
+ effects: effects2
21108
+ });
21041
21109
  const acceptedCount = typeof result?.acceptedCount === "number" ? result.acceptedCount : 0;
21042
21110
  const rejected = Array.isArray(result?.rejected) ? result.rejected : [];
21043
21111
  if (acceptedCount === 0 && rejected.length > 0) {
21044
- const detail = rejected.map((entry) => `${entry.name || "unknown"}: ${entry.reason || "rejected"}`).join("; ");
21112
+ const detail = rejected.map(
21113
+ (entry) => `${entry.name || "unknown"}: ${entry.reason || "rejected"}`
21114
+ ).join("; ");
21045
21115
  throw new Error(
21046
21116
  `Failed to publish live effects for sandbox ${host.sandboxId}: ${detail}`
21047
21117
  );
@@ -21075,13 +21145,15 @@ var Granular = class _Granular {
21075
21145
  disconnectError
21076
21146
  );
21077
21147
  }
21078
- void this.ensureSandboxEffectHost(host.sandboxId).catch((reconnectError) => {
21079
- console.error(
21080
- `[Granular] Failed to recover effect host for sandbox ${host.sandboxId} after heartbeat failure:`,
21081
- reconnectError
21082
- );
21083
- console.error("[Granular] Original heartbeat failure:", error2);
21084
- });
21148
+ void this.ensureSandboxEffectHost(host.sandboxId).catch(
21149
+ (reconnectError) => {
21150
+ console.error(
21151
+ `[Granular] Failed to recover effect host for sandbox ${host.sandboxId} after heartbeat failure:`,
21152
+ reconnectError
21153
+ );
21154
+ console.error("[Granular] Original heartbeat failure:", error2);
21155
+ }
21156
+ );
21085
21157
  }
21086
21158
  startEffectHostHeartbeat(host) {
21087
21159
  if (host.heartbeatTimer) {
@@ -21102,9 +21174,15 @@ var Granular = class _Granular {
21102
21174
  host.heartbeatInFlight = false;
21103
21175
  });
21104
21176
  };
21105
- sendHeartbeat("[Granular] Initial effect host heartbeat failed for sandbox", false);
21177
+ sendHeartbeat(
21178
+ "[Granular] Initial effect host heartbeat failed for sandbox",
21179
+ false
21180
+ );
21106
21181
  host.heartbeatTimer = setInterval(() => {
21107
- sendHeartbeat("[Granular] Effect host heartbeat failed for sandbox", true);
21182
+ sendHeartbeat(
21183
+ "[Granular] Effect host heartbeat failed for sandbox",
21184
+ true
21185
+ );
21108
21186
  }, 1e4);
21109
21187
  }
21110
21188
  stopEffectHostHeartbeat(host) {
@@ -21136,7 +21214,12 @@ var Granular = class _Granular {
21136
21214
  const effectClientId = crypto.randomUUID();
21137
21215
  const clientId = `effect-host:${sandboxId}:${effectClientId}`;
21138
21216
  const wsClient = new WSClient({
21139
- url: buildEffectHostUrl(this.apiUrl, sandboxId, effectClientId, clientId),
21217
+ url: buildEffectHostUrl(
21218
+ this.apiUrl,
21219
+ sandboxId,
21220
+ effectClientId,
21221
+ clientId
21222
+ ),
21140
21223
  sessionId: `effect-host:${effectClientId}`,
21141
21224
  token: this.apiKey,
21142
21225
  tokenProvider: this.tokenProvider,
@@ -21155,7 +21238,10 @@ var Granular = class _Granular {
21155
21238
  };
21156
21239
  wsClient.registerRpcHandler("effect.invoke", async (params) => {
21157
21240
  const request = params;
21158
- return invokeRegisteredEffect(this.getSandboxEffectMap(sandboxId), request);
21241
+ return invokeRegisteredEffect(
21242
+ this.getSandboxEffectMap(sandboxId),
21243
+ request
21244
+ );
21159
21245
  });
21160
21246
  wsClient.on("open", () => {
21161
21247
  void this.synchronizeEffectHost(host).catch((error2) => {
@@ -21203,7 +21289,7 @@ var Granular = class _Granular {
21203
21289
  }
21204
21290
  /**
21205
21291
  * Register multiple effects (tools) for a specific sandbox.
21206
- *
21292
+ *
21207
21293
  * batch version of `registerEffect`.
21208
21294
  */
21209
21295
  async registerEffects(sandboxNameOrId, effects2) {
@@ -21217,7 +21303,7 @@ var Granular = class _Granular {
21217
21303
  }
21218
21304
  /**
21219
21305
  * Unregister an effect from a sandbox.
21220
- *
21306
+ *
21221
21307
  * Removes it from the local sandbox registry and updates the
21222
21308
  * sandbox-scoped live catalog.
21223
21309
  */
@@ -21316,27 +21402,31 @@ var Granular = class _Granular {
21316
21402
  const assignments = await this.request(
21317
21403
  `/control/subjects/${subjectId}/assignments`
21318
21404
  );
21319
- const existing = assignments.items.find(
21320
- (a) => a.sandboxId === sandboxId
21321
- );
21405
+ const existing = assignments.items.find((a) => a.sandboxId === sandboxId);
21322
21406
  if (existing) {
21323
21407
  if (existing.permissionProfileId === permissionProfileId) {
21324
21408
  return;
21325
21409
  }
21326
- await this.request(`/control/assignments/${existing.assignmentId}`, {
21327
- method: "DELETE"
21328
- });
21410
+ await this.request(
21411
+ `/control/assignments/${existing.assignmentId}`,
21412
+ {
21413
+ method: "DELETE"
21414
+ }
21415
+ );
21329
21416
  }
21330
21417
  } catch {
21331
21418
  }
21332
- await this.request(`/control/subjects/${subjectId}/assignments`, {
21333
- method: "POST",
21334
- body: JSON.stringify({
21335
- sandboxId,
21336
- subjectId,
21337
- permissionProfileId
21338
- })
21339
- });
21419
+ await this.request(
21420
+ `/control/subjects/${subjectId}/assignments`,
21421
+ {
21422
+ method: "POST",
21423
+ body: JSON.stringify({
21424
+ sandboxId,
21425
+ subjectId,
21426
+ permissionProfileId
21427
+ })
21428
+ }
21429
+ );
21340
21430
  }
21341
21431
  /**
21342
21432
  * Sandbox management API
@@ -21416,23 +21506,33 @@ var Granular = class _Granular {
21416
21506
  },
21417
21507
  get: async (environmentId) => {
21418
21508
  return normalizeEnvironmentData(
21419
- await this.request(`/control/environments/${environmentId}`)
21509
+ await this.request(
21510
+ `/control/environments/${environmentId}`
21511
+ )
21420
21512
  );
21421
21513
  },
21422
21514
  create: async (sandboxId, data) => {
21423
21515
  const environmentName = data.environment || data.envName;
21424
- return normalizeEnvironmentData(await this.request(`/control/sandboxes/${sandboxId}/environments`, {
21425
- method: "POST",
21426
- body: JSON.stringify({
21427
- ...data,
21428
- envName: environmentName
21429
- })
21430
- }));
21516
+ return normalizeEnvironmentData(
21517
+ await this.request(
21518
+ `/control/sandboxes/${sandboxId}/environments`,
21519
+ {
21520
+ method: "POST",
21521
+ body: JSON.stringify({
21522
+ ...data,
21523
+ envName: environmentName
21524
+ })
21525
+ }
21526
+ )
21527
+ );
21431
21528
  },
21432
21529
  delete: async (environmentId) => {
21433
- return this.request(`/control/environments/${environmentId}`, {
21434
- method: "DELETE"
21435
- });
21530
+ return this.request(
21531
+ `/control/environments/${environmentId}`,
21532
+ {
21533
+ method: "DELETE"
21534
+ }
21535
+ );
21436
21536
  }
21437
21537
  };
21438
21538
  }
@@ -21452,10 +21552,13 @@ var Granular = class _Granular {
21452
21552
  }
21453
21553
  if (params.since) query.set("since", params.since.toISOString());
21454
21554
  if (params.until) query.set("until", params.until.toISOString());
21455
- if (params.isAcked !== void 0) query.set("isAcked", params.isAcked ? "1" : "0");
21555
+ if (params.isAcked !== void 0)
21556
+ query.set("isAcked", params.isAcked ? "1" : "0");
21456
21557
  if (params.limit) query.set("limit", String(params.limit));
21457
21558
  if (params.offset) query.set("offset", String(params.offset));
21458
- const result = await this.request(`/control/stream-events?${query.toString()}`);
21559
+ const result = await this.request(
21560
+ `/control/stream-events?${query.toString()}`
21561
+ );
21459
21562
  return (result.items || []).map((row) => ({
21460
21563
  eventId: row.event_id,
21461
21564
  streamName: row.stream_name,
@@ -21486,7 +21589,9 @@ var Granular = class _Granular {
21486
21589
  since: cursor,
21487
21590
  limit: 100
21488
21591
  });
21489
- const orderedEvents = [...events].sort((a, b) => a.createdAt - b.createdAt);
21592
+ const orderedEvents = [...events].sort(
21593
+ (a, b) => a.createdAt - b.createdAt
21594
+ );
21490
21595
  for (const event of orderedEvents) {
21491
21596
  if (seenEventIds.has(event.eventId)) {
21492
21597
  continue;
@@ -21499,15 +21604,19 @@ var Granular = class _Granular {
21499
21604
  params.onEvent(event);
21500
21605
  }
21501
21606
  } catch (err) {
21502
- params.onError?.(err instanceof Error ? err : new Error(String(err)));
21607
+ params.onError?.(
21608
+ err instanceof Error ? err : new Error(String(err))
21609
+ );
21503
21610
  }
21504
21611
  await new Promise((resolve2) => setTimeout(resolve2, interval));
21505
21612
  }
21506
21613
  };
21507
21614
  poll();
21508
- return { unsubscribe: () => {
21509
- running = false;
21510
- } };
21615
+ return {
21616
+ unsubscribe: () => {
21617
+ running = false;
21618
+ }
21619
+ };
21511
21620
  },
21512
21621
  ack: async (eventId) => {
21513
21622
  await this.request("/control/stream-events/ack", {
@@ -21525,7 +21634,9 @@ var Granular = class _Granular {
21525
21634
  const sandbox = await this._resolveSandboxId(params.ontology);
21526
21635
  const query = new URLSearchParams({ sandboxId: sandbox });
21527
21636
  if (params.environment) query.set("environmentId", params.environment);
21528
- const result = await this.request(`/control/stream-events/stats?${query.toString()}`);
21637
+ const result = await this.request(
21638
+ `/control/stream-events/stats?${query.toString()}`
21639
+ );
21529
21640
  return (result.items || []).map((row) => ({
21530
21641
  streamName: row.stream_name,
21531
21642
  eventType: row.event_type,
@@ -21543,10 +21654,14 @@ var Granular = class _Granular {
21543
21654
  get subjects() {
21544
21655
  return {
21545
21656
  get: async (subjectId) => {
21546
- return normalizeSubject(await this.request(`/control/subjects/${subjectId}`));
21657
+ return normalizeSubject(
21658
+ await this.request(`/control/subjects/${subjectId}`)
21659
+ );
21547
21660
  },
21548
21661
  listAssignments: async (subjectId) => {
21549
- return this.request(`/control/subjects/${subjectId}/assignments`);
21662
+ return this.request(
21663
+ `/control/subjects/${subjectId}/assignments`
21664
+ );
21550
21665
  }
21551
21666
  };
21552
21667
  }
@@ -21556,24 +21671,31 @@ var Granular = class _Granular {
21556
21671
  get users() {
21557
21672
  return {
21558
21673
  create: async (data) => {
21559
- return normalizeSubject(await this.request("/control/subjects", {
21560
- method: "POST",
21561
- body: JSON.stringify({
21562
- identityId: data.id,
21563
- name: data.name,
21564
- email: data.email
21674
+ return normalizeSubject(
21675
+ await this.request("/control/subjects", {
21676
+ method: "POST",
21677
+ body: JSON.stringify({
21678
+ identityId: data.id,
21679
+ name: data.name,
21680
+ email: data.email
21681
+ })
21565
21682
  })
21566
- }));
21683
+ );
21567
21684
  },
21568
21685
  get: async (id) => {
21569
- return normalizeSubject(await this.request(`/control/subjects/${id}`));
21686
+ return normalizeSubject(
21687
+ await this.request(`/control/subjects/${id}`)
21688
+ );
21570
21689
  }
21571
21690
  };
21572
21691
  }
21573
21692
  async _resolveSandboxId(ontologyNameOrId) {
21574
21693
  if (ontologyNameOrId.startsWith("sbx_")) return ontologyNameOrId;
21575
- const result = await this.request(`/control/sandboxes?name=${encodeURIComponent(ontologyNameOrId)}`);
21576
- if (result.items.length === 0) throw new Error(`Ontology not found: ${ontologyNameOrId}`);
21694
+ const result = await this.request(
21695
+ `/control/sandboxes?name=${encodeURIComponent(ontologyNameOrId)}`
21696
+ );
21697
+ if (result.items.length === 0)
21698
+ throw new Error(`Ontology not found: ${ontologyNameOrId}`);
21577
21699
  return result.items[0].sandboxId;
21578
21700
  }
21579
21701
  /**
@@ -21588,9 +21710,9 @@ var Granular = class _Granular {
21588
21710
  const response = await fetch(url, {
21589
21711
  ...options,
21590
21712
  headers: {
21591
- "Authorization": `Bearer ${this.apiKey}`,
21713
+ Authorization: `Bearer ${this.apiKey}`,
21592
21714
  "Content-Type": "application/json",
21593
- "Connection": "close",
21715
+ Connection: "close",
21594
21716
  ...options.headers
21595
21717
  }
21596
21718
  });
@@ -21601,7 +21723,11 @@ var Granular = class _Granular {
21601
21723
  return response.json();
21602
21724
  }
21603
21725
  const errorText = await response.text();
21604
- const retryable = isRetryableLocalWorkerRestart(response.status, errorText, url);
21726
+ const retryable = isRetryableLocalWorkerRestart(
21727
+ response.status,
21728
+ errorText,
21729
+ url
21730
+ );
21605
21731
  if (retryable && attempt < LOCAL_CONTROL_REQUEST_RETRY_COUNT) {
21606
21732
  if (this.debugHttp) {
21607
21733
  console.warn(