@granular-software/sdk 0.4.65 → 0.4.67

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.
@@ -1,5 +1,5 @@
1
- import { a as EnvironmentSession, G as Granular } from './client-S-z8bri1.mjs';
2
- import { aC as GranularSpendContext, ax as OpenAITokenSpend, l as Prompt, f as RecordObjectOptions, g as ManifestContent, k as SessionHeapSnapshot, T as ToolWithHandler, aY as ConnectOptions, bn as CreateEnvironmentData, aR as GranularOptions } from './spend-CESUvrZC.mjs';
1
+ import { a as EnvironmentSession, G as Granular } from './client-BkX2cb20.mjs';
2
+ import { aC as GranularSpendContext, ax as OpenAITokenSpend, l as Prompt, f as RecordObjectOptions, g as ManifestContent, k as SessionHeapSnapshot, T as ToolWithHandler, aY as ConnectOptions, bn as CreateEnvironmentData, aR as GranularOptions } from './spend-D4r0SA-H.mjs';
3
3
  import { GranularAgentToolInfo, GeneratedJobCodeIssue, BuildGranularAgentSystemPromptInput, HarnessRenderedPrompt, HarnessRenderedContinuation, HarnessControllerBudgets } from './agent-harness.mjs';
4
4
  import '@automerge/automerge';
5
5
  import '@automerge/automerge/slim';
@@ -1,5 +1,5 @@
1
- import { a as EnvironmentSession, G as Granular } from './client-z6shQivt.js';
2
- import { aC as GranularSpendContext, ax as OpenAITokenSpend, l as Prompt, f as RecordObjectOptions, g as ManifestContent, k as SessionHeapSnapshot, T as ToolWithHandler, aY as ConnectOptions, bn as CreateEnvironmentData, aR as GranularOptions } from './spend-CESUvrZC.js';
1
+ import { a as EnvironmentSession, G as Granular } from './client-BT1DJddL.js';
2
+ import { aC as GranularSpendContext, ax as OpenAITokenSpend, l as Prompt, f as RecordObjectOptions, g as ManifestContent, k as SessionHeapSnapshot, T as ToolWithHandler, aY as ConnectOptions, bn as CreateEnvironmentData, aR as GranularOptions } from './spend-D4r0SA-H.js';
3
3
  import { GranularAgentToolInfo, GeneratedJobCodeIssue, BuildGranularAgentSystemPromptInput, HarnessRenderedPrompt, HarnessRenderedContinuation, HarnessControllerBudgets } from './agent-harness.js';
4
4
  import '@automerge/automerge';
5
5
  import '@automerge/automerge/slim';
@@ -5282,7 +5282,6 @@ var DEFAULT_RPC_TIMEOUT_MS = 3e4;
5282
5282
  var DOMAIN_PACKAGE_RPC_TIMEOUT_MS = 12e4;
5283
5283
  var EFFECT_CONTROL_RPC_TIMEOUT_MS = 12e4;
5284
5284
  var HARNESS_RUN_RPC_TIMEOUT_MS = 6e5;
5285
- var HEARTBEAT_RPC_TIMEOUT_MS = 15e3;
5286
5285
  var DEFAULT_CONNECT_TIMEOUT_MS = 1e4;
5287
5286
  var DEFAULT_RECONNECT_DELAY_MS = 3e3;
5288
5287
  var DEFAULT_MAX_RECONNECT_ATTEMPTS = 5;
@@ -5328,8 +5327,6 @@ function rpcTimeoutMsForMethod(method) {
5328
5327
  case "domain.fetchPackagePart":
5329
5328
  case "domain.getSummary":
5330
5329
  return DOMAIN_PACKAGE_RPC_TIMEOUT_MS;
5331
- case "client.heartbeat":
5332
- return HEARTBEAT_RPC_TIMEOUT_MS;
5333
5330
  case "effects.publishCatalog":
5334
5331
  case "effects.resetCatalog":
5335
5332
  case "effects.addCatalog":
@@ -5349,6 +5346,7 @@ var WSClient = class {
5349
5346
  sessionId;
5350
5347
  token;
5351
5348
  messageQueue = [];
5349
+ pendingRpcPeriodStartedAt = null;
5352
5350
  rpcHandlers = /* @__PURE__ */ new Map();
5353
5351
  eventHandlers = /* @__PURE__ */ new Map();
5354
5352
  canonicalDocumentQuarantineHandlers = /* @__PURE__ */ new Set();
@@ -5363,6 +5361,7 @@ var WSClient = class {
5363
5361
  cancelConnectAttempt = null;
5364
5362
  documentRepairRequired = false;
5365
5363
  canonicalDocumentActivated = false;
5364
+ lastInboundActivityAt = null;
5366
5365
  options;
5367
5366
  constructor(options) {
5368
5367
  this.options = options;
@@ -5376,6 +5375,29 @@ var WSClient = class {
5376
5375
  get currentSessionId() {
5377
5376
  return this.sessionId;
5378
5377
  }
5378
+ isTransportOpen() {
5379
+ return this.ws?.readyState === READY_STATE_OPEN;
5380
+ }
5381
+ hasPendingRpcRequests() {
5382
+ return this.messageQueue.length > 0;
5383
+ }
5384
+ hasRecentlyPendingRpcPeriod(maxAgeMs, now = Date.now()) {
5385
+ return this.messageQueue.length > 0 && this.pendingRpcPeriodStartedAt !== null && now - this.pendingRpcPeriodStartedAt <= maxAgeMs;
5386
+ }
5387
+ removePendingRpc(id) {
5388
+ this.messageQueue = this.messageQueue.filter(
5389
+ (pending) => pending.id !== id
5390
+ );
5391
+ if (this.messageQueue.length === 0) this.pendingRpcPeriodStartedAt = null;
5392
+ }
5393
+ /**
5394
+ * Whether the current socket has delivered any frame recently. SessionDO
5395
+ * broadcasts a one-way heartbeat every five seconds, so this measures the
5396
+ * transport without adding an RPC that can be delayed by application work.
5397
+ */
5398
+ hasRecentInboundActivity(maxAgeMs, now = Date.now()) {
5399
+ return this.ws?.readyState === READY_STATE_OPEN && this.lastInboundActivityAt !== null && now - this.lastInboundActivityAt <= maxAgeMs;
5400
+ }
5379
5401
  seedDocumentSnapshot(document) {
5380
5402
  if (!document || typeof document !== "object" || Array.isArray(document)) {
5381
5403
  return;
@@ -5527,6 +5549,7 @@ var WSClient = class {
5527
5549
  const socket = this.ws;
5528
5550
  this.connectionEpoch += 1;
5529
5551
  this.ws = null;
5552
+ this.lastInboundActivityAt = null;
5530
5553
  try {
5531
5554
  socket?.close(4001, "Transport failure");
5532
5555
  } catch {
@@ -5609,12 +5632,14 @@ var WSClient = class {
5609
5632
  closeStaleSocket();
5610
5633
  return;
5611
5634
  }
5635
+ this.lastInboundActivityAt = Date.now();
5612
5636
  this.reconnectAttempts = 0;
5613
5637
  this.emit("open", {});
5614
5638
  finish();
5615
5639
  };
5616
5640
  const handleMessage = (data) => {
5617
5641
  if (!isCurrent()) return;
5642
+ this.lastInboundActivityAt = Date.now();
5618
5643
  try {
5619
5644
  const text = typeof data === "string" ? data : data && typeof data === "object" && "toString" in data ? String(data.toString()) : "";
5620
5645
  this.handleMessage(JSON.parse(text));
@@ -5649,9 +5674,7 @@ var WSClient = class {
5649
5674
  this.ws = null;
5650
5675
  closeStaleSocket();
5651
5676
  finish(
5652
- new Error(
5653
- `WebSocket connect timed out after ${connectTimeoutMs}ms`
5654
- )
5677
+ new Error(`WebSocket connect timed out after ${connectTimeoutMs}ms`)
5655
5678
  );
5656
5679
  }, connectTimeoutMs);
5657
5680
  signal?.addEventListener("abort", handleAbort, { once: true });
@@ -5691,6 +5714,7 @@ var WSClient = class {
5691
5714
  pending.reject(error);
5692
5715
  });
5693
5716
  this.messageQueue = [];
5717
+ this.pendingRpcPeriodStartedAt = null;
5694
5718
  }
5695
5719
  emitReconnectErrorMessage(error, terminal = false) {
5696
5720
  const reconnectInfo = {
@@ -6094,9 +6118,7 @@ var WSClient = class {
6094
6118
  } else {
6095
6119
  pending.resolve(response.result);
6096
6120
  }
6097
- this.messageQueue = this.messageQueue.filter(
6098
- (q) => q.id !== response.id
6099
- );
6121
+ this.removePendingRpc(response.id);
6100
6122
  }
6101
6123
  return;
6102
6124
  }
@@ -6134,16 +6156,24 @@ var WSClient = class {
6134
6156
  const timeout = setTimeout(() => {
6135
6157
  const pending = this.messageQueue.find((q) => q.id === id);
6136
6158
  if (pending) {
6137
- this.messageQueue = this.messageQueue.filter((q) => q.id !== id);
6159
+ this.removePendingRpc(id);
6138
6160
  reject(new Error(`RPC timeout: ${method}`));
6139
6161
  }
6140
6162
  }, timeoutMs);
6141
- this.messageQueue.push({ resolve, reject, id, timeout });
6163
+ if (this.messageQueue.length === 0) {
6164
+ this.pendingRpcPeriodStartedAt = Date.now();
6165
+ }
6166
+ this.messageQueue.push({
6167
+ resolve,
6168
+ reject,
6169
+ id,
6170
+ timeout
6171
+ });
6142
6172
  try {
6143
6173
  this.ws.send(JSON.stringify(request));
6144
6174
  } catch (error) {
6145
6175
  clearTimeout(timeout);
6146
- this.messageQueue = this.messageQueue.filter((q) => q.id !== id);
6176
+ this.removePendingRpc(id);
6147
6177
  reject(error instanceof Error ? error : new Error(String(error)));
6148
6178
  }
6149
6179
  });
@@ -6248,6 +6278,7 @@ var WSClient = class {
6248
6278
  this.cancelConnectAttempt?.();
6249
6279
  this.cancelConnectAttempt = null;
6250
6280
  this.connectionEpoch += 1;
6281
+ this.lastInboundActivityAt = null;
6251
6282
  if (this.reconnectTimer) {
6252
6283
  clearTimeout(this.reconnectTimer);
6253
6284
  this.reconnectTimer = null;
@@ -12664,6 +12695,7 @@ function mergeClassSummaryPatch(target, patch) {
12664
12695
  function mergePropertySummaryPatch(target, patch) {
12665
12696
  pushUnique(target.notes, patch.notes || []);
12666
12697
  if (patch.searchable !== void 0) target.searchable = patch.searchable;
12698
+ if (patch.format !== void 0) target.format = patch.format;
12667
12699
  if (patch.searchablePhonetic !== void 0) {
12668
12700
  target.searchablePhonetic = patch.searchablePhonetic;
12669
12701
  }
@@ -15500,6 +15532,91 @@ var stateMachineMetamodelPackage = defineMetamodelPackage({
15500
15532
  }
15501
15533
  });
15502
15534
 
15535
+ // ../metamodel-temporal/src/index.ts
15536
+ function normalizeTemporalFormat(format) {
15537
+ return format === "date" || format === "date-time" ? format : null;
15538
+ }
15539
+ function buildTemporalFieldMutations(fieldPath, format) {
15540
+ const normalized = normalizeTemporalFormat(format);
15541
+ if (!normalized) return [];
15542
+ return [
15543
+ {
15544
+ label: `set temporal format on ${fieldPath}`,
15545
+ query: `mutation { at(path: ${JSON.stringify(fieldPath)}) { set_temporal_format(format: ${JSON.stringify(normalized)}) { format } } }`
15546
+ }
15547
+ ];
15548
+ }
15549
+ var temporalMetamodelPackage = defineMetamodelPackage({
15550
+ id: "temporal",
15551
+ docs: {
15552
+ fieldRows: [
15553
+ {
15554
+ key: "format",
15555
+ description: "Temporal representation for date fields. Use `date` for a civil calendar date and `date-time` when the instant and time matter."
15556
+ }
15557
+ ]
15558
+ },
15559
+ graphql: {
15560
+ typeDefs: [
15561
+ `
15562
+ type TemporalFormatMetamodel {
15563
+ model: Model!
15564
+ format: String!
15565
+ }
15566
+
15567
+ extend type Model {
15568
+ temporal_format: TemporalFormatMetamodel
15569
+ }
15570
+
15571
+ extend type ModelMutation {
15572
+ set_temporal_format(format: String!): TemporalFormatMetamodel
15573
+ }
15574
+ `
15575
+ ],
15576
+ createResolvers({ run }) {
15577
+ return {
15578
+ TemporalFormatMetamodel: {
15579
+ model: (value) => value.model,
15580
+ format: (value) => value.format
15581
+ },
15582
+ Model: {
15583
+ temporal_format: async (ant) => await run(ant.temporal_format())
15584
+ },
15585
+ ModelMutation: {
15586
+ set_temporal_format: async (ant, { format }) => {
15587
+ if (format !== "date" && format !== "date-time") {
15588
+ throw new Error(`Unsupported temporal format: ${format}`);
15589
+ }
15590
+ return {
15591
+ model: await run(ant.set_temporal_format(format)),
15592
+ format
15593
+ };
15594
+ }
15595
+ }
15596
+ };
15597
+ }
15598
+ },
15599
+ manifest: {
15600
+ buildFieldMutations(fieldPath, spec) {
15601
+ return buildTemporalFieldMutations(fieldPath, spec.format);
15602
+ }
15603
+ },
15604
+ summary: {
15605
+ selections: {
15606
+ propertyFields: ["temporal_format { format }"]
15607
+ },
15608
+ readPropertySummary(rawProperty) {
15609
+ const format = rawProperty.temporal_format?.format;
15610
+ return format === "date" || format === "date-time" ? { format } : {};
15611
+ }
15612
+ },
15613
+ domain: {
15614
+ applyToPropertyIR(propertyIR, _propertySummary) {
15615
+ return propertyIR;
15616
+ }
15617
+ }
15618
+ });
15619
+
15503
15620
  // ../metamodel-validation-rule/src/index.ts
15504
15621
  function describeRule(rule) {
15505
15622
  if (rule.message) return rule.message;
@@ -15671,6 +15788,7 @@ var DEFAULT_METAMODEL_PACKAGES = [
15671
15788
  searchableMetamodelPackage,
15672
15789
  validationRuleMetamodelPackage,
15673
15790
  stateMachineMetamodelPackage,
15791
+ temporalMetamodelPackage,
15674
15792
  effectBehaviorsMetamodelPackage,
15675
15793
  policyMetamodelPackage
15676
15794
  ];
@@ -15993,16 +16111,100 @@ function normalizeEnvironmentData(environment) {
15993
16111
  setup: normalizeEnvironmentSetupSummary(environment.setup)
15994
16112
  };
15995
16113
  }
16114
+ function isGraphQLMutationDocument(query) {
16115
+ if (typeof query !== "string") return false;
16116
+ const tokens = [];
16117
+ for (let index = 0; index < query.length; ) {
16118
+ const character = query[index];
16119
+ if (/[,\s\ufeff]/.test(character)) {
16120
+ index += 1;
16121
+ continue;
16122
+ }
16123
+ if (character === "#") {
16124
+ while (index < query.length && !/[\r\n]/.test(query[index])) index += 1;
16125
+ continue;
16126
+ }
16127
+ if (character === '"') {
16128
+ const block = query.startsWith('"""', index);
16129
+ let closed = false;
16130
+ index += block ? 3 : 1;
16131
+ while (index < query.length) {
16132
+ if (block && query.startsWith('"""', index) && query[index - 1] !== "\\") {
16133
+ index += 3;
16134
+ closed = true;
16135
+ break;
16136
+ }
16137
+ if (!block && query[index] === '"') {
16138
+ index += 1;
16139
+ closed = true;
16140
+ break;
16141
+ }
16142
+ index += query[index] === "\\" && !block ? 2 : 1;
16143
+ }
16144
+ if (!closed) return true;
16145
+ continue;
16146
+ }
16147
+ const name = query.slice(index).match(/^[_A-Za-z][_0-9A-Za-z]*/)?.[0];
16148
+ if (name) {
16149
+ tokens.push(name);
16150
+ index += name.length;
16151
+ continue;
16152
+ }
16153
+ if ("{}()[]".includes(character)) tokens.push(character);
16154
+ index += 1;
16155
+ }
16156
+ const skipSelection = (start) => {
16157
+ let depth = 0;
16158
+ for (let index = start; index < tokens.length; index += 1) {
16159
+ if (tokens[index] === "{") depth += 1;
16160
+ if (tokens[index] === "}" && --depth === 0) return index + 1;
16161
+ }
16162
+ return null;
16163
+ };
16164
+ for (let index = 0; index < tokens.length; ) {
16165
+ const definition = tokens[index++];
16166
+ if (definition === "mutation") return true;
16167
+ if (definition === "{") {
16168
+ const nextDefinition2 = skipSelection(index - 1);
16169
+ if (nextDefinition2 === null) return true;
16170
+ index = nextDefinition2;
16171
+ continue;
16172
+ }
16173
+ if (!["query", "subscription", "fragment"].includes(definition || "")) {
16174
+ return true;
16175
+ }
16176
+ let parentheses = 0;
16177
+ let brackets = 0;
16178
+ while (index < tokens.length) {
16179
+ const token = tokens[index];
16180
+ if (token === "(") parentheses += 1;
16181
+ else if (token === ")") parentheses -= 1;
16182
+ else if (token === "[") brackets += 1;
16183
+ else if (token === "]") brackets -= 1;
16184
+ else if (token === "{" && parentheses === 0 && brackets === 0) break;
16185
+ index += 1;
16186
+ }
16187
+ if (index === tokens.length) return true;
16188
+ const nextDefinition = skipSelection(index);
16189
+ if (nextDefinition === null) return true;
16190
+ index = nextDefinition;
16191
+ }
16192
+ return false;
16193
+ }
15996
16194
  var Environment = class _Environment {
15997
16195
  granular;
15998
16196
  envData;
15999
16197
  _apiKey;
16000
16198
  _apiEndpoint;
16001
- constructor(granular, envData, apiKey, apiEndpoint) {
16199
+ _tokenProvider;
16200
+ _beforeMutationSend;
16201
+ constructor(granular, envData, apiKey, apiEndpoint, tokenProvider, beforeMutationSend) {
16002
16202
  this.granular = granular;
16003
16203
  this.envData = envData;
16004
16204
  this._apiKey = apiKey;
16005
16205
  this._apiEndpoint = apiEndpoint;
16206
+ this._tokenProvider = tokenProvider;
16207
+ this._beforeMutationSend = beforeMutationSend;
16006
16208
  }
16007
16209
  /** The environment ID */
16008
16210
  get environmentId() {
@@ -16068,6 +16270,15 @@ var Environment = class _Environment {
16068
16270
  get authToken() {
16069
16271
  return this._apiKey;
16070
16272
  }
16273
+ resolveAuthToken() {
16274
+ if (!this._tokenProvider) return this._apiKey;
16275
+ return Promise.resolve(this._tokenProvider()).then((provided) => {
16276
+ if (typeof provided === "string" && provided.trim()) {
16277
+ this._apiKey = provided.trim();
16278
+ }
16279
+ return this._apiKey;
16280
+ });
16281
+ }
16071
16282
  /** Base runtime URL derived from the GraphQL endpoint */
16072
16283
  get runtimeBaseUrl() {
16073
16284
  return this.getRuntimeBaseUrl();
@@ -16459,10 +16670,15 @@ var Environment = class _Environment {
16459
16670
  async controlPlaneRequest(path2, options = {}) {
16460
16671
  const runtimeBase = this.getRuntimeBaseUrl();
16461
16672
  const url = `${runtimeBase}${path2}`;
16673
+ const authToken = await this.resolveAuthToken();
16674
+ const method = (options.method || "GET").toUpperCase();
16675
+ if (method !== "GET" && method !== "HEAD" && method !== "OPTIONS") {
16676
+ this._beforeMutationSend?.();
16677
+ }
16462
16678
  const response = await fetch(url, {
16463
16679
  ...options,
16464
16680
  headers: {
16465
- Authorization: `Bearer ${this._apiKey}`,
16681
+ Authorization: `Bearer ${authToken}`,
16466
16682
  "Content-Type": "application/json",
16467
16683
  ...options.headers
16468
16684
  }
@@ -16527,11 +16743,15 @@ var Environment = class _Environment {
16527
16743
  * ```
16528
16744
  */
16529
16745
  async graphql(query, variables) {
16746
+ const authToken = await this.resolveAuthToken();
16747
+ if (this._beforeMutationSend && isGraphQLMutationDocument(query)) {
16748
+ this._beforeMutationSend?.();
16749
+ }
16530
16750
  const response = await fetch(this._apiEndpoint, {
16531
16751
  method: "POST",
16532
16752
  headers: {
16533
16753
  "Content-Type": "application/json",
16534
- Authorization: `Bearer ${this._apiKey}`
16754
+ Authorization: `Bearer ${authToken}`
16535
16755
  },
16536
16756
  body: JSON.stringify({
16537
16757
  environmentId: this.environmentId,
@@ -17010,8 +17230,10 @@ var Environment = class _Environment {
17010
17230
  eventType.payloadSchema.properties
17011
17231
  )) {
17012
17232
  const schema = propSchema;
17233
+ const format = schema.format === "date" || schema.format === "date-time" ? schema.format : void 0;
17013
17234
  fieldSpecs[propName] = {
17014
17235
  type: schema.type ?? "string",
17236
+ ...format ? { format } : {},
17015
17237
  description: schema.description
17016
17238
  };
17017
17239
  }
@@ -17433,40 +17655,36 @@ var EnvironmentSession = class extends Session {
17433
17655
  environment;
17434
17656
  sessionDataRoutePrefix;
17435
17657
  sessionDataHeaders;
17658
+ beforeMutationSend;
17436
17659
  heartbeatTimer = null;
17437
- heartbeatInFlight = false;
17438
- /** The last known graph container status, updated by checkReadiness() or on heartbeat */
17660
+ /** The last known graph container status, updated by checkReadiness(). */
17439
17661
  graphContainerStatus = null;
17440
17662
  constructor(client, environment, clientId, options = {}) {
17441
17663
  super(client, clientId, { initialQuota: options.initialQuota });
17442
17664
  this.environment = environment;
17443
17665
  this.sessionDataRoutePrefix = options.sessionDataRoutePrefix || "/orchestrator/ws/sessions";
17444
17666
  this.sessionDataHeaders = options.sessionDataHeaders || {};
17667
+ this.beforeMutationSend = options.beforeMutationSend;
17445
17668
  this.setFeedListTransport(
17446
17669
  (feedOptions) => this.sessionDataRequest("/feed", feedOptions)
17447
17670
  );
17448
17671
  }
17449
17672
  /**
17450
17673
  * Keep the browser session transport observable from the client side.
17451
- * A worker/proxy restart can leave a browser WebSocket appearing OPEN even
17452
- * though the server-side Durable Object has already closed its peer. The
17453
- * heartbeat gives the SDK a bounded failure signal so it can revoke that
17454
- * stale socket and use WSClient's normal reconnect path.
17674
+ * SessionDO already broadcasts a heartbeat every five seconds. Treat that
17675
+ * and all other inbound frames as proof of life instead of issuing an RPC:
17676
+ * an application RPC may be delayed by Durable Object work even while the
17677
+ * underlying WebSocket is healthy.
17455
17678
  */
17456
17679
  startHeartbeat() {
17457
17680
  if (this.heartbeatTimer) return;
17458
17681
  this.heartbeatTimer = setInterval(() => {
17459
- if (this.heartbeatInFlight) return;
17460
- this.heartbeatInFlight = true;
17461
- void this.client.call("client.heartbeat", {}).then((result) => {
17462
- if (result?.graphContainerStatus) {
17463
- this.graphContainerStatus = result.graphContainerStatus;
17464
- }
17465
- }).catch((error) => {
17466
- this.client.reportTransportFailure(error);
17467
- }).finally(() => {
17468
- this.heartbeatInFlight = false;
17469
- });
17682
+ if (!this.client.isTransportOpen()) return;
17683
+ if (this.client.hasRecentInboundActivity(3e4)) return;
17684
+ if (this.client.hasRecentlyPendingRpcPeriod(6e4)) return;
17685
+ this.client.reportTransportFailure(
17686
+ new Error("No server activity received for 30 seconds")
17687
+ );
17470
17688
  }, 5e3);
17471
17689
  this.heartbeatTimer.unref?.();
17472
17690
  }
@@ -17475,7 +17693,6 @@ var EnvironmentSession = class extends Session {
17475
17693
  clearInterval(this.heartbeatTimer);
17476
17694
  this.heartbeatTimer = null;
17477
17695
  }
17478
- this.heartbeatInFlight = false;
17479
17696
  }
17480
17697
  async hello() {
17481
17698
  const result = await super.hello();
@@ -17536,7 +17753,13 @@ var EnvironmentSession = class extends Session {
17536
17753
  for (let attempt = 1; attempt <= SESSION_DATA_REQUEST_RETRY_COUNT; attempt += 1) {
17537
17754
  try {
17538
17755
  const headers = new Headers(init2.headers);
17539
- headers.set("Authorization", `Bearer ${this.environment.authToken}`);
17756
+ const resolvedAuthToken = this.environment.resolveAuthToken();
17757
+ const authToken = typeof resolvedAuthToken === "string" ? resolvedAuthToken : await resolvedAuthToken;
17758
+ headers.set("Authorization", `Bearer ${authToken}`);
17759
+ const method = (init2.method || "GET").toUpperCase();
17760
+ if (method !== "GET" && method !== "HEAD" && method !== "OPTIONS") {
17761
+ this.beforeMutationSend?.();
17762
+ }
17540
17763
  const response = await fetch(url, {
17541
17764
  method: init2.method || "GET",
17542
17765
  headers,
@@ -17995,7 +18218,7 @@ var EnvironmentSession = class extends Session {
17995
18218
  method: "POST",
17996
18219
  headers: {
17997
18220
  "Content-Type": "application/json",
17998
- Authorization: `Bearer ${this.environment.authToken}`
18221
+ Authorization: `Bearer ${await this.environment.resolveAuthToken()}`
17999
18222
  },
18000
18223
  body: JSON.stringify({
18001
18224
  reason: "sdk_disconnect_http_fallback",
@@ -18787,6 +19010,9 @@ var Granular = class _Granular {
18787
19010
  if (typeof options.limit === "number") {
18788
19011
  query.set("limit", String(options.limit));
18789
19012
  }
19013
+ if (options.consistency === "refresh") {
19014
+ query.set("consistency", "refresh");
19015
+ }
18790
19016
  const state = await this.request(
18791
19017
  `/sdk/user-environment-state?${query.toString()}`
18792
19018
  );
@@ -18930,6 +19156,10 @@ var Granular = class _Granular {
18930
19156
  });
18931
19157
  }
18932
19158
  async recordOpenAIUsageSpend(usage, context, options) {
19159
+ const providedToken = await this.tokenProvider?.();
19160
+ if (typeof providedToken === "string" && providedToken.trim()) {
19161
+ this.apiKey = providedToken.trim();
19162
+ }
18933
19163
  return recordOpenAIUsageSpend({
18934
19164
  apiUrl: this.apiUrl,
18935
19165
  token: this.apiKey,
@@ -19102,7 +19332,13 @@ var Granular = class _Granular {
19102
19332
  }
19103
19333
  bindEnvironmentHandle(envData) {
19104
19334
  const graphqlEndpoint = `${this.httpUrl}/orchestrator/graphql`;
19105
- return new Environment(this, envData, this.apiKey, graphqlEndpoint);
19335
+ return new Environment(
19336
+ this,
19337
+ envData,
19338
+ this.apiKey,
19339
+ graphqlEndpoint,
19340
+ this.tokenProvider
19341
+ );
19106
19342
  }
19107
19343
  async bindWebSocketEnvironmentSession(environment, clientId, session, transportOptions = {}) {
19108
19344
  const client = new WSClient({
@@ -19986,6 +20222,10 @@ var Granular = class _Granular {
19986
20222
  console.log(`[SDK] Requesting: ${url}`);
19987
20223
  }
19988
20224
  for (let attempt = 1; attempt <= LOCAL_CONTROL_REQUEST_RETRY_COUNT; attempt += 1) {
20225
+ const providedToken = await this.tokenProvider?.();
20226
+ if (typeof providedToken === "string" && providedToken.trim()) {
20227
+ this.apiKey = providedToken.trim();
20228
+ }
19989
20229
  const response = await fetch(url, {
19990
20230
  ...options,
19991
20231
  headers: {
@@ -21917,6 +22157,10 @@ function jsonSchemaTypeName(schema) {
21917
22157
  const record = normalizeJsonSchema(schema);
21918
22158
  if (!record) return "unknown";
21919
22159
  const type = record.type;
22160
+ if (record.format === "date-time") return "date-time";
22161
+ if (record.format === "date" || type === "date" || Array.isArray(type) && type.includes("date")) {
22162
+ return "date";
22163
+ }
21920
22164
  if (typeof type === "string") {
21921
22165
  if (type === "array") return "array";
21922
22166
  if (type === "object") return "object";
@@ -22418,6 +22662,8 @@ ${actionIndex}
22418
22662
  - A suggestion is a recommendation button: \`await stateHandle.suggest(message)\`. A prepared action is the editable form the user can review/run: \`const action = stateHandle.reach(); const prepared = await action.open()\`.
22419
22663
  - Use suggestions when the user asks "what can I do next?", asks for options, or gives an unclear intent. Prefer 2 to 5 concrete suggestions and keep text short.
22420
22664
  - Opening a prepared action does not execute the underlying mutation. Use \`.open()\` only when the user asks to prepare, review, show, or edit an action before running it, when a new record must be prepared, or when a reviewable form must collect missing editable inputs. Prefill only grounded values and leave unknown fields empty.
22665
+ - For creation inputs, use the declared action schema and its field descriptions as the mapping authority. Copy every compatible value explicitly present in the current request, conversation, selected/saved object, file, fresh read, or declared ontology default, including values for fields with arbitrary names. Omit only values for which that context provides no evidence.
22666
+ - Never fill an input because of its field name, a regular expression or other string pattern, its primitive type, a schema minimum, an example value, or the first enum option. If the evidence is ambiguous, leave the field unresolved or ask the user.
22421
22667
  - When the user explicitly commands an existing-record mutation such as approve, reject, block, route, send, or update, and one visible domain action uniquely matches, call that domain action directly. Do not substitute a state-handle \`.open()\` artifact for execution. If runtime policy requires confirmation, invoke the action once and let the runtime pause and resume that same invocation.
22422
22668
  - When a grounded related/context record can satisfy the prepared action through declared relationships, use those relationships to fill required relationship inputs before opening or updating the action. If a required relationship remains empty, continue through declared relationship chains from the grounded object when the next hop can fill that slot. Do not only save the context record in memory while leaving derivable relationship slots blank.
22423
22669
  - A derived intermediate relationship is not enough when another required relationship is still reachable from it. For example, if a team gives a cost center and the action also requires a budget, traverse the cost center's declared budget relationship before opening the action.