@granular-software/sdk 0.4.33 → 0.4.35

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
@@ -19956,6 +19956,50 @@ function computeEffectKey(effect) {
19956
19956
  }
19957
19957
  return effect.static ? `class:${attachedClass}:static:${effect.name}` : `class:${attachedClass}:instance:${effect.name}`;
19958
19958
  }
19959
+ function computeEffectVersionSelectorSpecificity(selector) {
19960
+ if (!selector || selector.mode === "all") {
19961
+ return 0;
19962
+ }
19963
+ if (selector.mode === "exact") {
19964
+ return 2;
19965
+ }
19966
+ return 1;
19967
+ }
19968
+ function matchesEffectVersionSelector(selector, buildVersionNumber) {
19969
+ if (!selector || selector.mode === "all") {
19970
+ return true;
19971
+ }
19972
+ if (typeof buildVersionNumber !== "number" || !Number.isFinite(buildVersionNumber)) {
19973
+ return false;
19974
+ }
19975
+ if (selector.mode === "exact") {
19976
+ return buildVersionNumber === selector.versionNumber;
19977
+ }
19978
+ if (selector.mode === "before") {
19979
+ return buildVersionNumber < selector.versionNumber;
19980
+ }
19981
+ return buildVersionNumber > selector.versionNumber;
19982
+ }
19983
+ function selectRegisteredEffect(effectMap, effectKey, buildVersionNumber) {
19984
+ let bestEffect;
19985
+ let bestSpecificity = Number.NEGATIVE_INFINITY;
19986
+ for (const effect of effectMap.values()) {
19987
+ if (computeEffectKey(effect) !== effectKey) {
19988
+ continue;
19989
+ }
19990
+ if (!matchesEffectVersionSelector(effect.versionSelector, buildVersionNumber)) {
19991
+ continue;
19992
+ }
19993
+ const specificity = computeEffectVersionSelectorSpecificity(
19994
+ effect.versionSelector
19995
+ );
19996
+ if (!bestEffect || specificity > bestSpecificity) {
19997
+ bestEffect = effect;
19998
+ bestSpecificity = specificity;
19999
+ }
20000
+ }
20001
+ return bestEffect;
20002
+ }
19959
20003
  function normalizeEffectBehaviors(value) {
19960
20004
  return normalizeEffectBehaviorSummary(
19961
20005
  value
@@ -19976,9 +20020,17 @@ function resolveReverseEffect(effectMap, currentEffect, request, behaviors) {
19976
20020
  return void 0;
19977
20021
  }
19978
20022
  if (reverseHandler.includes(":")) {
19979
- return effectMap.get(reverseHandler);
20023
+ return selectRegisteredEffect(
20024
+ effectMap,
20025
+ reverseHandler,
20026
+ request.context?.buildVersionNumber
20027
+ );
19980
20028
  }
19981
- const directMatch = effectMap.get(reverseHandler);
20029
+ const directMatch = selectRegisteredEffect(
20030
+ effectMap,
20031
+ reverseHandler,
20032
+ request.context?.buildVersionNumber
20033
+ );
19982
20034
  if (directMatch) {
19983
20035
  return directMatch;
19984
20036
  }
@@ -19993,7 +20045,11 @@ function resolveReverseEffect(effectMap, currentEffect, request, behaviors) {
19993
20045
  })
19994
20046
  ];
19995
20047
  for (const candidateKey of candidateKeys) {
19996
- const candidate = effectMap.get(candidateKey);
20048
+ const candidate = selectRegisteredEffect(
20049
+ effectMap,
20050
+ candidateKey,
20051
+ request.context?.buildVersionNumber
20052
+ );
19997
20053
  if (candidate) {
19998
20054
  return candidate;
19999
20055
  }
@@ -20029,7 +20085,11 @@ function resolveHandlerForMode(effectMap, effect, request) {
20029
20085
  return { effect, mode, handler: effect.handler };
20030
20086
  }
20031
20087
  async function invokeRegisteredEffect(effectMap, request) {
20032
- const effect = effectMap.get(request.effectKey);
20088
+ const effect = selectRegisteredEffect(
20089
+ effectMap,
20090
+ request.effectKey,
20091
+ request.context?.buildVersionNumber
20092
+ );
20033
20093
  if (!effect) {
20034
20094
  throw new Error(`Effect handler not found: ${request.effectKey}`);
20035
20095
  }
@@ -20150,6 +20210,17 @@ function computeEffectKey2(effect) {
20150
20210
  }
20151
20211
  return effect.static ? `class:${attachedClass}:static:${effect.name}` : `class:${attachedClass}:instance:${effect.name}`;
20152
20212
  }
20213
+ function computeEffectVersionSelectorKey(selector) {
20214
+ if (!selector || selector.mode === "all") {
20215
+ return "all";
20216
+ }
20217
+ return `${selector.mode}:${selector.versionNumber}`;
20218
+ }
20219
+ function computeEffectRegistrationKey(effect) {
20220
+ return `${computeEffectKey2(effect)}@${computeEffectVersionSelectorKey(
20221
+ effect.versionSelector
20222
+ )}`;
20223
+ }
20153
20224
  function buildEffectHostUrl(apiUrl, sandboxId, effectClientId, clientId) {
20154
20225
  const url = new URL(apiUrl);
20155
20226
  if (url.pathname.endsWith("/granular/ws/connect")) {
@@ -20233,6 +20304,38 @@ function normalizeUser(user) {
20233
20304
  permissions: Array.isArray(user.permissions) ? user.permissions : []
20234
20305
  };
20235
20306
  }
20307
+ function normalizeEnvironmentSetupSummary(setup) {
20308
+ if (!setup) {
20309
+ return null;
20310
+ }
20311
+ const queuedRecords = Number(setup.queuedRecords || 0);
20312
+ const processingRecords = Number(setup.processingRecords || 0);
20313
+ return {
20314
+ ...setup,
20315
+ setupRunId: String(setup.setupRunId || ""),
20316
+ environmentId: String(setup.environmentId || ""),
20317
+ sandboxId: String(setup.sandboxId || ""),
20318
+ subjectId: String(setup.subjectId || ""),
20319
+ triggerReason: setup.triggerReason === "fresh_after_version_update" ? "fresh_after_version_update" : "new_environment",
20320
+ lifecycleStatus: setup.lifecycleStatus === "completed" || setup.lifecycleStatus === "failed" ? setup.lifecycleStatus : "running",
20321
+ stage: typeof setup.stage === "string" ? setup.stage : null,
20322
+ totalObjectsToImport: Number(setup.totalObjectsToImport || 0),
20323
+ totalImports: Number(setup.totalImports || 0),
20324
+ activeImports: Number(setup.activeImports || 0),
20325
+ totalRecords: Number(setup.totalRecords || 0),
20326
+ queuedRecords,
20327
+ processingRecords,
20328
+ completedRecords: Number(setup.completedRecords || 0),
20329
+ failedRecords: Number(setup.failedRecords || 0),
20330
+ canceledRecords: Number(setup.canceledRecords || 0),
20331
+ awaitingRecords: Number(setup.awaitingRecords || 0) || queuedRecords + processingRecords,
20332
+ errorMessage: typeof setup.errorMessage === "string" ? setup.errorMessage : null,
20333
+ startedAt: Number(setup.startedAt || Date.now()),
20334
+ hookCompletedAt: setup.hookCompletedAt == null ? null : Number(setup.hookCompletedAt),
20335
+ finishedAt: setup.finishedAt == null ? null : Number(setup.finishedAt),
20336
+ updatedAt: Number(setup.updatedAt || Date.now())
20337
+ };
20338
+ }
20236
20339
  function normalizeEnvironmentData(environment) {
20237
20340
  const buildPolicy = environment.buildPolicy || environment.tracking || (environment.tagId ? { mode: "tag", tagId: environment.tagId } : {
20238
20341
  mode: "pinned",
@@ -20246,7 +20349,8 @@ function normalizeEnvironmentData(environment) {
20246
20349
  envName: environmentName,
20247
20350
  environment: environmentName,
20248
20351
  buildPolicy,
20249
- tracking: environment.tracking || buildPolicy
20352
+ tracking: environment.tracking || buildPolicy,
20353
+ setup: normalizeEnvironmentSetupSummary(environment.setup)
20250
20354
  };
20251
20355
  }
20252
20356
  var Environment = class {
@@ -20304,6 +20408,10 @@ var Environment = class {
20304
20408
  get updateState() {
20305
20409
  return this.envData.updateState;
20306
20410
  }
20411
+ /** The latest setup/import run summary for this environment, when available. */
20412
+ get setup() {
20413
+ return this.envData.setup || null;
20414
+ }
20307
20415
  /** Convenience flag for whether this environment trails the current tag target */
20308
20416
  get isOutdated() {
20309
20417
  return this.envData.updateState === "update_available";
@@ -20324,6 +20432,9 @@ var Environment = class {
20324
20432
  get runtimeBaseUrl() {
20325
20433
  return this.getRuntimeBaseUrl();
20326
20434
  }
20435
+ syncEnvironmentData(envData) {
20436
+ this.envData = normalizeEnvironmentData(envData);
20437
+ }
20327
20438
  get sessions() {
20328
20439
  return {
20329
20440
  list: async (options) => this.listSessions(options?.status || "active"),
@@ -21269,7 +21380,8 @@ var Environment = class {
21269
21380
  method: "POST",
21270
21381
  body: JSON.stringify({
21271
21382
  records,
21272
- batchSize: options.batchSize
21383
+ batchSize: options.batchSize,
21384
+ setupRunId: options.setupRunId
21273
21385
  })
21274
21386
  }
21275
21387
  );
@@ -21417,18 +21529,12 @@ var EnvironmentSession = class extends Session {
21417
21529
  }
21418
21530
  get messages() {
21419
21531
  return {
21420
- list: (options = {}) => this.sessionDataRequest(
21421
- "/messages",
21422
- options
21423
- )
21532
+ list: (options = {}) => this.sessionDataRequest("/messages", options)
21424
21533
  };
21425
21534
  }
21426
21535
  get timeline() {
21427
21536
  return {
21428
- list: (options = {}) => this.sessionDataRequest(
21429
- "/timeline",
21430
- options
21431
- )
21537
+ list: (options = {}) => this.sessionDataRequest("/timeline", options)
21432
21538
  };
21433
21539
  }
21434
21540
  get jobs() {
@@ -21445,10 +21551,7 @@ var EnvironmentSession = class extends Session {
21445
21551
  get heap() {
21446
21552
  return {
21447
21553
  entries: {
21448
- list: (options = {}) => this.sessionDataRequest(
21449
- "/heap/entries",
21450
- options
21451
- ),
21554
+ list: (options = {}) => this.sessionDataRequest("/heap/entries", options),
21452
21555
  get: (path6) => this.sessionDataRequest(
21453
21556
  `/heap/entries/${encodeURIComponent(path6)}`
21454
21557
  )
@@ -21492,10 +21595,7 @@ var EnvironmentSession = class extends Session {
21492
21595
  const heap = normalizeHeapSnapshot({
21493
21596
  entriesByPath: Object.fromEntries(
21494
21597
  entries.map((entry) => {
21495
- return entry?.path ? [
21496
- entry.path,
21497
- entry
21498
- ] : null;
21598
+ return entry?.path ? [entry.path, entry] : null;
21499
21599
  }).filter(
21500
21600
  (entry) => Boolean(entry)
21501
21601
  )
@@ -21661,6 +21761,15 @@ var OntologyHandle = class {
21661
21761
  disconnect: async () => this.granular.disconnectEffects(this.ontologyNameOrId)
21662
21762
  };
21663
21763
  }
21764
+ get importer() {
21765
+ return {
21766
+ onEnvironmentCreate: (handler) => this.granular.registerEnvironmentImporter(
21767
+ this.ontologyNameOrId,
21768
+ handler
21769
+ ),
21770
+ clear: () => this.granular.clearEnvironmentImporter(this.ontologyNameOrId)
21771
+ };
21772
+ }
21664
21773
  };
21665
21774
  var Granular = class _Granular {
21666
21775
  apiKey;
@@ -21671,12 +21780,16 @@ var Granular = class _Granular {
21671
21780
  onUnexpectedClose;
21672
21781
  onReconnectError;
21673
21782
  debugHttp = process.env.GRANULAR_DEBUG_HTTP === "1";
21674
- /** Sandbox-level effect registry: sandboxId → (effectKey → ToolWithHandler) */
21783
+ /** Sandbox-level effect registry: sandboxId → (effectKey@selector → ToolWithHandler) */
21675
21784
  sandboxEffects = /* @__PURE__ */ new Map();
21676
21785
  /** Live sandbox-scoped effect hosts keyed by sandboxId */
21677
21786
  sandboxEffectHosts = /* @__PURE__ */ new Map();
21678
21787
  /** In-flight host connection promises to avoid duplicate concurrent connects */
21679
21788
  sandboxEffectHostPromises = /* @__PURE__ */ new Map();
21789
+ /** Ontology-bound environment importer hooks keyed by the caller's ontology identifier. */
21790
+ ontologyImporters = /* @__PURE__ */ new Map();
21791
+ /** Resolved importer hooks keyed by sandboxId for fast lookups during openEnvironment(). */
21792
+ sandboxImporters = /* @__PURE__ */ new Map();
21680
21793
  /**
21681
21794
  * Create a new Granular client
21682
21795
  * @param options - Client configuration
@@ -21702,6 +21815,18 @@ var Granular = class _Granular {
21702
21815
  ontology(ontologyNameOrId) {
21703
21816
  return new OntologyHandle(this, ontologyNameOrId);
21704
21817
  }
21818
+ registerEnvironmentImporter(ontologyNameOrId, handler) {
21819
+ this.ontologyImporters.set(ontologyNameOrId, handler);
21820
+ if (ontologyNameOrId.startsWith("sbx_")) {
21821
+ this.sandboxImporters.set(ontologyNameOrId, handler);
21822
+ }
21823
+ }
21824
+ clearEnvironmentImporter(ontologyNameOrId) {
21825
+ this.ontologyImporters.delete(ontologyNameOrId);
21826
+ if (ontologyNameOrId.startsWith("sbx_")) {
21827
+ this.sandboxImporters.delete(ontologyNameOrId);
21828
+ }
21829
+ }
21705
21830
  /**
21706
21831
  * Records/upserts a user and prepares them for sandbox connections
21707
21832
  *
@@ -21818,11 +21943,13 @@ var Granular = class _Granular {
21818
21943
  * ```
21819
21944
  */
21820
21945
  async openEnvironment(options) {
21821
- const envData = await this.resolveOpenEnvironmentData(
21946
+ const resolved = await this.resolveOpenEnvironmentData(
21822
21947
  options,
21823
21948
  "openEnvironment"
21824
21949
  );
21825
- return this.bindEnvironmentHandle(envData);
21950
+ const environment = this.bindEnvironmentHandle(resolved.environment);
21951
+ await this.maybeRunEnvironmentImporter(resolved, environment);
21952
+ return environment;
21826
21953
  }
21827
21954
  /**
21828
21955
  * Deprecated compatibility alias for `openEnvironment()`.
@@ -21909,7 +22036,12 @@ var Granular = class _Granular {
21909
22036
  )
21910
22037
  );
21911
22038
  if (currentMatches.length > 0) {
21912
- return currentMatches[0];
22039
+ return {
22040
+ environment: currentMatches[0],
22041
+ requestedOntology: ontology,
22042
+ sandboxId: sandbox.sandboxId,
22043
+ subjectId: user.granularId
22044
+ };
21913
22045
  }
21914
22046
  const outdatedMatches = this.sortEnvironmentsByRecency(
21915
22047
  userEnvironments.filter(
@@ -21917,14 +22049,25 @@ var Granular = class _Granular {
21917
22049
  )
21918
22050
  );
21919
22051
  if (outdatedMatches.length > 0 && options.createFreshIfOutdated !== true) {
21920
- return outdatedMatches[0];
22052
+ return {
22053
+ environment: outdatedMatches[0],
22054
+ requestedOntology: ontology,
22055
+ sandboxId: sandbox.sandboxId,
22056
+ subjectId: user.granularId
22057
+ };
21921
22058
  }
21922
- return this.environments.create(sandbox.sandboxId, {
22059
+ return {
22060
+ environment: await this.environments.create(sandbox.sandboxId, {
22061
+ subjectId: user.granularId,
22062
+ environment: this.buildManagedEnvironmentName(tagName, targetVersionId),
22063
+ tagId: tag2.tagId,
22064
+ permissionProfileId: null
22065
+ }),
22066
+ requestedOntology: ontology,
22067
+ sandboxId: sandbox.sandboxId,
21923
22068
  subjectId: user.granularId,
21924
- environment: this.buildManagedEnvironmentName(tagName, targetVersionId),
21925
- tagId: tag2.tagId,
21926
- permissionProfileId: null
21927
- });
22069
+ setupTriggerReason: outdatedMatches.length > 0 ? "fresh_after_version_update" : "new_environment"
22070
+ };
21928
22071
  }
21929
22072
  /**
21930
22073
  * List active (open) sessions for an environment — each session is one agent conversation thread.
@@ -22041,6 +22184,80 @@ var Granular = class _Granular {
22041
22184
  });
22042
22185
  return this.connectSession({ sessionId, clientId: options?.clientId });
22043
22186
  }
22187
+ resolveEnvironmentImporter(requestedOntology, sandboxId) {
22188
+ const resolved = this.sandboxImporters.get(sandboxId) || this.ontologyImporters.get(requestedOntology);
22189
+ if (resolved && !this.sandboxImporters.has(sandboxId) && this.ontologyImporters.get(requestedOntology) === resolved) {
22190
+ this.sandboxImporters.set(sandboxId, resolved);
22191
+ }
22192
+ return resolved;
22193
+ }
22194
+ async maybeRunEnvironmentImporter(resolved, environment) {
22195
+ if (!resolved.setupTriggerReason) {
22196
+ return;
22197
+ }
22198
+ const importer = this.resolveEnvironmentImporter(
22199
+ resolved.requestedOntology,
22200
+ resolved.sandboxId
22201
+ );
22202
+ if (!importer) {
22203
+ return;
22204
+ }
22205
+ const setupRun = await this.request(
22206
+ `/control/environments/${environment.environmentId}/setup-runs`,
22207
+ {
22208
+ method: "POST",
22209
+ body: JSON.stringify({
22210
+ triggerReason: resolved.setupTriggerReason
22211
+ })
22212
+ }
22213
+ );
22214
+ const setupRunId = setupRun.setupRunId;
22215
+ const updateSetupRun = async (patch) => {
22216
+ await this.request(
22217
+ `/control/environment-setup-runs/${setupRunId}`,
22218
+ {
22219
+ method: "PATCH",
22220
+ body: JSON.stringify(patch)
22221
+ }
22222
+ );
22223
+ };
22224
+ const importerContext = {
22225
+ environmentId: environment.environmentId,
22226
+ sandboxId: environment.sandboxId,
22227
+ subjectId: environment.subjectId,
22228
+ reason: resolved.setupTriggerReason,
22229
+ incrementTotalObjectsToImportCount: async (n) => {
22230
+ const safeIncrement = Math.max(0, Math.trunc(n));
22231
+ if (safeIncrement <= 0) {
22232
+ return;
22233
+ }
22234
+ await updateSetupRun({
22235
+ incrementTotalObjectsToImportCount: safeIncrement
22236
+ });
22237
+ },
22238
+ setStage: async (stage) => {
22239
+ await updateSetupRun({ stage });
22240
+ },
22241
+ importRecords: async (records, options) => environment.enqueueRecordImport(records, {
22242
+ batchSize: options?.batchSize,
22243
+ setupRunId
22244
+ })
22245
+ };
22246
+ try {
22247
+ await importer(importerContext);
22248
+ await updateSetupRun({ markHookCompleted: true });
22249
+ const refreshedEnvironment = await this.environments.get(
22250
+ environment.environmentId
22251
+ );
22252
+ environment.syncEnvironmentData(refreshedEnvironment);
22253
+ } catch (error2) {
22254
+ await updateSetupRun({
22255
+ status: "failed",
22256
+ errorMessage: error2 instanceof Error ? error2.message : String(error2)
22257
+ }).catch(() => void 0);
22258
+ throw error2;
22259
+ }
22260
+ }
22044
22261
  bindEnvironmentHandle(envData) {
22045
22262
  const graphqlEndpoint = `${this.httpUrl}/orchestrator/graphql`;
22046
22263
  return new Environment(this, envData, this.apiKey, graphqlEndpoint);
@@ -22090,7 +22307,8 @@ var Granular = class _Granular {
22090
22307
  provenance: effect.provenance || { source: "custom" },
22091
22308
  tags: effect.tags,
22092
22309
  className: effect.className,
22093
- static: effect.static
22310
+ static: effect.static,
22311
+ versionSelector: effect.versionSelector
22094
22312
  };
22095
22313
  }
22096
22314
  async publishSandboxEffectCatalog(host) {
@@ -22278,7 +22496,10 @@ var Granular = class _Granular {
22278
22496
  async registerEffect(sandboxNameOrId, effect) {
22279
22497
  const sandbox = await this.findOrCreateSandbox(sandboxNameOrId);
22280
22498
  const sandboxId = sandbox.sandboxId;
22281
- this.getSandboxEffectMap(sandboxId).set(computeEffectKey2(effect), effect);
22499
+ this.getSandboxEffectMap(sandboxId).set(
22500
+ computeEffectRegistrationKey(effect),
22501
+ effect
22502
+ );
22282
22503
  await this.syncSandboxEffectCatalog(sandboxId);
22283
22504
  }
22284
22505
  /**
@@ -22291,7 +22512,7 @@ var Granular = class _Granular {
22291
22512
  const sandboxId = sandbox.sandboxId;
22292
22513
  const map = this.getSandboxEffectMap(sandboxId);
22293
22514
  for (const effect of effects2) {
22294
- map.set(computeEffectKey2(effect), effect);
22515
+ map.set(computeEffectRegistrationKey(effect), effect);
22295
22516
  }
22296
22517
  await this.syncSandboxEffectCatalog(sandboxId);
22297
22518
  }
@@ -22309,7 +22530,7 @@ var Granular = class _Granular {
22309
22530
  return;
22310
22531
  }
22311
22532
  const nextEntries = Array.from(currentMap.entries()).filter(
22312
- ([effectKey, effect]) => effectKey !== name && effect.name !== name
22533
+ ([, effect]) => computeEffectKey2(effect) !== name && effect.name !== name
22313
22534
  );
22314
22535
  if (nextEntries.length === currentMap.size) {
22315
22536
  return;
@@ -303,6 +303,7 @@ interface EnvironmentData {
303
303
  sandboxId: string;
304
304
  ontologyId?: string;
305
305
  buildId: string;
306
+ versionNumber?: number | null;
306
307
  versionId: string;
307
308
  subjectId: string;
308
309
  envName: string;
@@ -312,6 +313,7 @@ interface EnvironmentData {
312
313
  tag?: VersionTag | null;
313
314
  tracking?: BuildPolicy;
314
315
  buildPolicy: BuildPolicy;
316
+ setup?: EnvironmentSetupSummary | null;
315
317
  updateState?: "up_to_date" | "update_available" | "upgrading" | "failed";
316
318
  createdAt: number;
317
319
  updatedAt: number;
@@ -422,6 +424,8 @@ interface EffectHandlerContext {
422
424
  effectClientId: string;
423
425
  sandboxId: string;
424
426
  environmentId: string;
427
+ buildId?: string;
428
+ buildVersionNumber?: number;
425
429
  sessionId: string;
426
430
  tenantId?: string;
427
431
  principalId?: string;
@@ -534,6 +538,12 @@ interface ToolSchema {
534
538
  * passed as the first argument to the handler).
535
539
  */
536
540
  static?: boolean;
541
+ /**
542
+ * Optional build-version selector for this live effect binding.
543
+ *
544
+ * When omitted, the binding applies to all build versions for the sandbox.
545
+ */
546
+ versionSelector?: EffectVersionSelector;
537
547
  /** Declarative runtime behaviors attached to the effect. */
538
548
  metamodels?: ManifestEffectMetamodelSpec;
539
549
  }
@@ -566,6 +576,18 @@ interface PublishToolsResult {
566
576
  }>;
567
577
  }
568
578
  type PublishEffectsResult = PublishToolsResult;
579
+ type EffectVersionSelector = {
580
+ mode: "all";
581
+ } | {
582
+ mode: "exact";
583
+ versionNumber: number;
584
+ } | {
585
+ mode: "before";
586
+ versionNumber: number;
587
+ } | {
588
+ mode: "after";
589
+ versionNumber: number;
590
+ };
569
591
  /**
570
592
  * Domain state response
571
593
  */
@@ -603,6 +625,8 @@ interface ToolInfo {
603
625
  className?: string;
604
626
  /** Whether this is a static method */
605
627
  static?: boolean;
628
+ /** Optional build-version selector associated with the live binding. */
629
+ versionSelector?: EffectVersionSelector;
606
630
  /** Declarative runtime behaviors attached to the effect. */
607
631
  metamodels?: ManifestEffectMetamodelSpec;
608
632
  }
@@ -1082,6 +1106,7 @@ interface RecordImport {
1082
1106
  environmentId: string;
1083
1107
  sandboxId: string;
1084
1108
  subjectId: string;
1109
+ setupRunId?: string | null;
1085
1110
  status: RecordImportStatus;
1086
1111
  batchSize: number;
1087
1112
  errorMessage: string | null;
@@ -1098,6 +1123,37 @@ interface EnvironmentRecordImportSummary extends RecordImportStats {
1098
1123
  activeImports: number;
1099
1124
  updatedAt: number;
1100
1125
  }
1126
+ type EnvironmentSetupTriggerReason = "new_environment" | "fresh_after_version_update";
1127
+ type EnvironmentSetupLifecycleStatus = "running" | "completed" | "failed";
1128
+ interface EnvironmentSetupSummary extends RecordImportStats {
1129
+ setupRunId: string;
1130
+ environmentId: string;
1131
+ sandboxId: string;
1132
+ subjectId: string;
1133
+ triggerReason: EnvironmentSetupTriggerReason;
1134
+ lifecycleStatus: EnvironmentSetupLifecycleStatus;
1135
+ stage: string | null;
1136
+ totalObjectsToImport: number;
1137
+ totalImports: number;
1138
+ activeImports: number;
1139
+ errorMessage: string | null;
1140
+ startedAt: number;
1141
+ hookCompletedAt: number | null;
1142
+ finishedAt: number | null;
1143
+ updatedAt: number;
1144
+ }
1145
+ interface EnvironmentImporterImportOptions {
1146
+ batchSize?: number;
1147
+ }
1148
+ interface EnvironmentImporter {
1149
+ environmentId: string;
1150
+ sandboxId: string;
1151
+ subjectId: string;
1152
+ reason: EnvironmentSetupTriggerReason;
1153
+ incrementTotalObjectsToImportCount: (n: number) => Promise<void>;
1154
+ setStage: (stage: string | null) => Promise<void>;
1155
+ importRecords: (records: RecordObjectOptions[], options?: EnvironmentImporterImportOptions) => Promise<RecordImport>;
1156
+ }
1101
1157
  /**
1102
1158
  * Property specification in a manifest operation
1103
1159
  */
@@ -1554,6 +1610,7 @@ declare class Session {
1554
1610
  private checkForToolChanges;
1555
1611
  }
1556
1612
 
1613
+ type EnvironmentImporterHandler = (importer: EnvironmentImporter) => Promise<void> | void;
1557
1614
  /**
1558
1615
  * Environment is the sessionless handle for one resolved ontology environment.
1559
1616
  *
@@ -1589,6 +1646,8 @@ declare class Environment {
1589
1646
  get buildPolicy(): BuildPolicy;
1590
1647
  /** The current update state relative to the followed tag */
1591
1648
  get updateState(): EnvironmentData["updateState"];
1649
+ /** The latest setup/import run summary for this environment, when available. */
1650
+ get setup(): EnvironmentSetupSummary | null;
1592
1651
  /** Convenience flag for whether this environment trails the current tag target */
1593
1652
  get isOutdated(): boolean;
1594
1653
  /** The followed tag name when this environment is tag-tracked */
@@ -1599,6 +1658,7 @@ declare class Environment {
1599
1658
  get authToken(): string;
1600
1659
  /** Base runtime URL derived from the GraphQL endpoint */
1601
1660
  get runtimeBaseUrl(): string;
1661
+ syncEnvironmentData(envData: EnvironmentData): void;
1602
1662
  get sessions(): {
1603
1663
  list: (options?: {
1604
1664
  status?: "active" | "closed" | "all";
@@ -1903,6 +1963,7 @@ declare class Environment {
1903
1963
  */
1904
1964
  enqueueRecordImport(records: RecordObjectOptions[], options?: {
1905
1965
  batchSize?: number;
1966
+ setupRunId?: string;
1906
1967
  }): Promise<RecordImport>;
1907
1968
  /**
1908
1969
  * List queued or completed record imports for this environment.
@@ -2058,6 +2119,10 @@ declare class OntologyHandle {
2058
2119
  clear: () => Promise<void>;
2059
2120
  disconnect: () => Promise<void>;
2060
2121
  };
2122
+ get importer(): {
2123
+ onEnvironmentCreate: (handler: EnvironmentImporterHandler) => void;
2124
+ clear: () => void;
2125
+ };
2061
2126
  }
2062
2127
  declare class Granular {
2063
2128
  private apiKey;
@@ -2068,12 +2133,16 @@ declare class Granular {
2068
2133
  private onUnexpectedClose?;
2069
2134
  private onReconnectError?;
2070
2135
  private debugHttp;
2071
- /** Sandbox-level effect registry: sandboxId → (effectKey → ToolWithHandler) */
2136
+ /** Sandbox-level effect registry: sandboxId → (effectKey@selector → ToolWithHandler) */
2072
2137
  private sandboxEffects;
2073
2138
  /** Live sandbox-scoped effect hosts keyed by sandboxId */
2074
2139
  private sandboxEffectHosts;
2075
2140
  /** In-flight host connection promises to avoid duplicate concurrent connects */
2076
2141
  private sandboxEffectHostPromises;
2142
+ /** Ontology-bound environment importer hooks keyed by the caller's ontology identifier. */
2143
+ private ontologyImporters;
2144
+ /** Resolved importer hooks keyed by sandboxId for fast lookups during openEnvironment(). */
2145
+ private sandboxImporters;
2077
2146
  /**
2078
2147
  * Create a new Granular client
2079
2148
  * @param options - Client configuration
@@ -2083,6 +2152,8 @@ declare class Granular {
2083
2152
  * Return an ontology-scoped handle for effects and other ontology-level APIs.
2084
2153
  */
2085
2154
  ontology(ontologyNameOrId: string): OntologyHandle;
2155
+ registerEnvironmentImporter(ontologyNameOrId: string, handler: EnvironmentImporterHandler): void;
2156
+ clearEnvironmentImporter(ontologyNameOrId: string): void;
2086
2157
  /**
2087
2158
  * Records/upserts a user and prepares them for sandbox connections
2088
2159
  *
@@ -2181,6 +2252,8 @@ declare class Granular {
2181
2252
  reopenSession(sessionId: string, options?: {
2182
2253
  clientId?: string;
2183
2254
  }): Promise<EnvironmentSession>;
2255
+ private resolveEnvironmentImporter;
2256
+ private maybeRunEnvironmentImporter;
2184
2257
  private bindEnvironmentHandle;
2185
2258
  private bindWebSocketEnvironmentSession;
2186
2259
  private activateEnvironment;
@@ -2326,4 +2399,4 @@ declare class Granular {
2326
2399
  private request;
2327
2400
  }
2328
2401
 
2329
- export { type SemanticVersionDiff as $, type AccessTokenProvider as A, type BuildPolicy as B, type ConnectOptions as C, type DomainState as D, type EndpointMode as E, type VersionTag as F, Granular as G, type EnvironmentData as H, type InstanceToolHandler as I, type CreateEnvironmentData as J, type EnvironmentListResponse as K, type Manifest as L, type ManifestEffectMetamodelSpec as M, type ManifestListResponse as N, OntologyHandle as O, type Prompt as P, type BuildStatus as Q, type ResolvedEffectBehaviors as R, type SessionHeapEntry as S, type ToolWithHandler as T, type User as U, type VersionTracking as V, WSClient as W, type Build as X, type Version as Y, type BuildListResponse as Z, type SemanticVersionDiffEntry as _, type EffectHandlerContext as a, type ManifestValidationRuleSpec as a$, type ResolvedEffectPostCondition as a0, type ResolvedEffectDryRun as a1, type ResolvedEffectReverse as a2, type ResolvedEffectApprovalRequired as a3, type EffectInvocationMode as a4, type EffectInvocationMetadata as a5, type EffectSchema as a6, type EffectWithHandler as a7, type PublishEffectsResult as a8, type ToolInfo as a9, type SessionCollectionListResult as aA, type WSDisconnectInfo as aB, type WSReconnectErrorInfo as aC, type WSClientOptions as aD, type RPCRequest as aE, type RPCResponse as aF, type SyncMessage as aG, type RPCRequestFromServer as aH, type ToolInvokeParams as aI, type ToolResultParams as aJ, type ModelRef as aK, type RelationshipInfo as aL, type DefineRelationshipOptions as aM, type RecordObjectOptions as aN, type RecordObjectResult as aO, type RecordObjectsChunkInfo as aP, type RecordObjectsOptions as aQ, type RecordImportStatus as aR, type RecordImportItemStatus as aS, type RecordImportStats as aT, type RecordImportItem as aU, type RecordImport as aV, type EnvironmentRecordImportSummary as aW, type ManifestPropertySpec as aX, type ManifestValidationOperator as aY, type ManifestEnumRuleSpec as aZ, type ManifestFilterBySpec as a_, type EffectInfo as aa, type ToolsChangedEvent as ab, type EffectsChangedEvent as ac, type EffectHandler as ad, type InstanceEffectHandler as ae, type JobStatus as af, type JobFeedbackSentiment as ag, type JobFeedbackToolCall as ah, type JobFeedbackMetadata as ai, type JobFeedbackInput as aj, type JobFeedbackRecord as ak, type EnvironmentFeedbackRecord as al, type JobSubmitResult as am, type Job as an, type ConversationMessageShowRefs as ao, type ConversationMessageInput as ap, type ConversationAppendResult as aq, type SessionConversationMessage as ar, type SessionTimelineEvent as as, type SessionJobRecord as at, type SessionHeapFieldType as au, type SessionHeapFieldValue as av, type SessionHeapVariable as aw, type SessionDocumentResult as ax, type SessionCollectionListOptions as ay, type SessionJobListOptions as az, type SessionHeapList as b, type ManifestStateMachineStateSpec as b0, type ManifestStateMachineTransitionSpec as b1, type ManifestStateMachineSpec as b2, type ManifestPostConditionSpec as b3, type ManifestDryRunSpec as b4, type ManifestReverseSpec as b5, type ManifestApprovalRequiredSpec as b6, type ManifestRelationshipDef as b7, type ManifestEffectSchema as b8, type ManifestEffectDeclaration as b9, type ManifestEventTypeDef as ba, type ManifestEventStreamDef as bb, type ManifestOperation as bc, type ManifestImport as bd, type ManifestVolume as be, type ManifestContent as bf, type GraphQLResult as bg, type APIError as bh, type DeleteResponse as bi, type StreamEvent as bj, type StreamSubscription as bk, type StreamStats as bl, type SessionHeapSnapshot as c, type SessionTranscriptEntry as d, Environment as e, EnvironmentSession as f, Session as g, type ToolSchema as h, type PublishToolsResult as i, type ToolHandler as j, type GranularOptions as k, type GranularAuth as l, type RecordUserOptions as m, type Subject as n, type OpenEnvironmentOptions as o, type CreateSessionOptions as p, type ConversationSessionInfo as q, type Sandbox as r, type CreateSandboxData as s, type SandboxListResponse as t, type PermissionRules as u, type PermissionProfile as v, type CreatePermissionProfileData as w, type PermissionProfileListResponse as x, type Assignment as y, type AssignmentListResponse as z };
2402
+ export { type SemanticVersionDiff as $, type AccessTokenProvider as A, type BuildPolicy as B, type ConnectOptions as C, type DomainState as D, type EndpointMode as E, type VersionTag as F, Granular as G, type EnvironmentData as H, type InstanceToolHandler as I, type CreateEnvironmentData as J, type EnvironmentListResponse as K, type Manifest as L, type ManifestEffectMetamodelSpec as M, type ManifestListResponse as N, OntologyHandle as O, type Prompt as P, type BuildStatus as Q, type ResolvedEffectBehaviors as R, type SessionHeapEntry as S, type ToolWithHandler as T, type User as U, type VersionTracking as V, WSClient as W, type Build as X, type Version as Y, type BuildListResponse as Z, type SemanticVersionDiffEntry as _, type EffectHandlerContext as a, type EnvironmentImporterImportOptions as a$, type ResolvedEffectPostCondition as a0, type ResolvedEffectDryRun as a1, type ResolvedEffectReverse as a2, type ResolvedEffectApprovalRequired as a3, type EffectInvocationMode as a4, type EffectInvocationMetadata as a5, type EffectSchema as a6, type EffectWithHandler as a7, type PublishEffectsResult as a8, type EffectVersionSelector as a9, type SessionJobListOptions as aA, type SessionCollectionListResult as aB, type WSDisconnectInfo as aC, type WSReconnectErrorInfo as aD, type WSClientOptions as aE, type RPCRequest as aF, type RPCResponse as aG, type SyncMessage as aH, type RPCRequestFromServer as aI, type ToolInvokeParams as aJ, type ToolResultParams as aK, type ModelRef as aL, type RelationshipInfo as aM, type DefineRelationshipOptions as aN, type RecordObjectOptions as aO, type RecordObjectResult as aP, type RecordObjectsChunkInfo as aQ, type RecordObjectsOptions as aR, type RecordImportStatus as aS, type RecordImportItemStatus as aT, type RecordImportStats as aU, type RecordImportItem as aV, type RecordImport as aW, type EnvironmentRecordImportSummary as aX, type EnvironmentSetupTriggerReason as aY, type EnvironmentSetupLifecycleStatus as aZ, type EnvironmentSetupSummary as a_, type ToolInfo as aa, type EffectInfo as ab, type ToolsChangedEvent as ac, type EffectsChangedEvent as ad, type EffectHandler as ae, type InstanceEffectHandler as af, type JobStatus as ag, type JobFeedbackSentiment as ah, type JobFeedbackToolCall as ai, type JobFeedbackMetadata as aj, type JobFeedbackInput as ak, type JobFeedbackRecord as al, type EnvironmentFeedbackRecord as am, type JobSubmitResult as an, type Job as ao, type ConversationMessageShowRefs as ap, type ConversationMessageInput as aq, type ConversationAppendResult as ar, type SessionConversationMessage as as, type SessionTimelineEvent as at, type SessionJobRecord as au, type SessionHeapFieldType as av, type SessionHeapFieldValue as aw, type SessionHeapVariable as ax, type SessionDocumentResult as ay, type SessionCollectionListOptions as az, type SessionHeapList as b, type EnvironmentImporter as b0, type ManifestPropertySpec as b1, type ManifestValidationOperator as b2, type ManifestEnumRuleSpec as b3, type ManifestFilterBySpec as b4, type ManifestValidationRuleSpec as b5, type ManifestStateMachineStateSpec as b6, type ManifestStateMachineTransitionSpec as b7, type ManifestStateMachineSpec as b8, type ManifestPostConditionSpec as b9, type ManifestDryRunSpec as ba, type ManifestReverseSpec as bb, type ManifestApprovalRequiredSpec as bc, type ManifestRelationshipDef as bd, type ManifestEffectSchema as be, type ManifestEffectDeclaration as bf, type ManifestEventTypeDef as bg, type ManifestEventStreamDef as bh, type ManifestOperation as bi, type ManifestImport as bj, type ManifestVolume as bk, type ManifestContent as bl, type GraphQLResult as bm, type APIError as bn, type DeleteResponse as bo, type StreamEvent as bp, type StreamSubscription as bq, type StreamStats as br, type SessionHeapSnapshot as c, type SessionTranscriptEntry as d, Environment as e, EnvironmentSession as f, Session as g, type ToolSchema as h, type PublishToolsResult as i, type ToolHandler as j, type GranularOptions as k, type GranularAuth as l, type RecordUserOptions as m, type Subject as n, type OpenEnvironmentOptions as o, type CreateSessionOptions as p, type ConversationSessionInfo as q, type Sandbox as r, type CreateSandboxData as s, type SandboxListResponse as t, type PermissionRules as u, type PermissionProfile as v, type CreatePermissionProfileData as w, type PermissionProfileListResponse as x, type Assignment as y, type AssignmentListResponse as z };