@granular-software/sdk 0.4.61 → 0.4.63

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -420,6 +420,14 @@ Built-in profiles are materialized by `granular init` and `granular pull`:
420
420
  - `confirm-all`: exposes every declared action but requires confirmation.
421
421
  - `deny-all`: exposes no executable actions.
422
422
 
423
+ `openEnvironment({ permissions: [...] })` resolves each entry by exact profile
424
+ name or profile ID. Unknown entries fail before the SDK writes an assignment;
425
+ they are never generated as permissive profiles. The only compatibility
426
+ exception is the explicit `allow-all` name, which the SDK may provision when it
427
+ is missing. Sync custom, `confirm-all`, and `deny-all` profiles with
428
+ `granular build` or `granular deploy` first. Map application roles to reviewed
429
+ profile names or IDs instead of forwarding unchecked end-user input.
430
+
423
431
  Manifest-level logic policies live on effects and apply to every profile:
424
432
 
425
433
  ```json
@@ -1,5 +1,5 @@
1
- import { a as EnvironmentSession, G as Granular } from './client-DSkGcmmX.mjs';
2
- import { at as GranularSpendContext, ao as OpenAITokenSpend, P as Prompt, cX as RecordObjectOptions, dN as ManifestContent, d as SessionHeapSnapshot, T as ToolWithHandler, aP as ConnectOptions, bd as CreateEnvironmentData, aI as GranularOptions } from './spend-CNe-Ff6U.mjs';
1
+ import { a as EnvironmentSession, G as Granular } from './client-lk-dmdKR.mjs';
2
+ import { at as GranularSpendContext, ao as OpenAITokenSpend, P as Prompt, cY as RecordObjectOptions, dO as ManifestContent, d as SessionHeapSnapshot, T as ToolWithHandler, aP as ConnectOptions, be as CreateEnvironmentData, aI as GranularOptions } from './spend-DtsHRNb5.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-BzgQvNyG.js';
2
- import { at as GranularSpendContext, ao as OpenAITokenSpend, P as Prompt, cX as RecordObjectOptions, dN as ManifestContent, d as SessionHeapSnapshot, T as ToolWithHandler, aP as ConnectOptions, bd as CreateEnvironmentData, aI as GranularOptions } from './spend-CNe-Ff6U.js';
1
+ import { a as EnvironmentSession, G as Granular } from './client-KqJIH7WE.js';
2
+ import { at as GranularSpendContext, ao as OpenAITokenSpend, P as Prompt, cY as RecordObjectOptions, dO as ManifestContent, d as SessionHeapSnapshot, T as ToolWithHandler, aP as ConnectOptions, be as CreateEnvironmentData, aI as GranularOptions } from './spend-DtsHRNb5.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';
@@ -14762,6 +14762,15 @@ var stateMachineMetamodelPackage = defineMetamodelPackage({
14762
14762
  is_final: Boolean!
14763
14763
  history: [StateMachineTransitionEvent!]!
14764
14764
  instances_in_state(state: String!): [Model!]!
14765
+ instance_population(state: String!, page: Int = 1, per_page: Int = 50): StateMachineInstancePopulation!
14766
+ }
14767
+
14768
+ type StateMachineInstancePopulation {
14769
+ count: Int!
14770
+ instances: [Model!]!
14771
+ page: Int!
14772
+ per_page: Int!
14773
+ has_more: Boolean!
14765
14774
  }
14766
14775
 
14767
14776
  type StateMachineState {
@@ -14970,6 +14979,13 @@ var stateMachineMetamodelPackage = defineMetamodelPackage({
14970
14979
  value.model.target || value.model,
14971
14980
  value.name,
14972
14981
  state
14982
+ ),
14983
+ instance_population: async (value, { state, page, per_page }) => await stateMachines.instancePopulation(
14984
+ value.model.target || value.model,
14985
+ value.name,
14986
+ state,
14987
+ page,
14988
+ per_page
14973
14989
  )
14974
14990
  }
14975
14991
  };
@@ -15218,7 +15234,6 @@ function buildEffectMetamodelMutations(toolPath, spec) {
15218
15234
  // src/client.ts
15219
15235
  var DEFAULT_CONVERSATION_SESSION_LIST_LIMIT = 100;
15220
15236
  var MAX_CONVERSATION_SESSION_LIST_LIMIT = 500;
15221
- var MAX_CONVERSATION_SESSION_LIST_OFFSET = 1e5;
15222
15237
  function requireUserEnvironmentSequence(value, field) {
15223
15238
  if (typeof value !== "number" || !Number.isSafeInteger(value) || value < 0) {
15224
15239
  throw new Error(
@@ -15598,6 +15613,10 @@ var Environment = class _Environment {
15598
15613
  get sessions() {
15599
15614
  return {
15600
15615
  list: async (options = {}) => this.listSessions(options),
15616
+ page: async (options = {}) => this.granular.listSessionsPage({
15617
+ ...options,
15618
+ environmentId: this.environmentId
15619
+ }),
15601
15620
  create: async (options) => this.createSession(options),
15602
15621
  connect: async (sessionId, options) => this.connectSession(sessionId, options),
15603
15622
  reopen: async (sessionId, options) => this.reopenSession(sessionId, options),
@@ -15853,7 +15872,6 @@ var Environment = class _Environment {
15853
15872
  headers: {
15854
15873
  Authorization: `Bearer ${this._apiKey}`,
15855
15874
  "Content-Type": "application/json",
15856
- Connection: "close",
15857
15875
  ...options.headers
15858
15876
  }
15859
15877
  });
@@ -17377,8 +17395,7 @@ var EnvironmentSession = class extends Session {
17377
17395
  method: "POST",
17378
17396
  headers: {
17379
17397
  "Content-Type": "application/json",
17380
- Authorization: `Bearer ${this.environment.authToken}`,
17381
- Connection: "close"
17398
+ Authorization: `Bearer ${this.environment.authToken}`
17382
17399
  },
17383
17400
  body: JSON.stringify({
17384
17401
  reason: "sdk_disconnect_http_fallback",
@@ -17906,11 +17923,16 @@ var Granular = class _Granular {
17906
17923
  `${methodName}() requires at least one permission so the SDK can ensure assignments for new users.`
17907
17924
  );
17908
17925
  }
17909
- for (const profileName of user.permissions) {
17910
- const profileId = await this.ensurePermissionProfile(
17911
- sandbox.sandboxId,
17912
- profileName
17926
+ const permissionProfileIds = [];
17927
+ for (const profileReference of user.permissions) {
17928
+ permissionProfileIds.push(
17929
+ await this.ensurePermissionProfile(
17930
+ sandbox.sandboxId,
17931
+ profileReference
17932
+ )
17913
17933
  );
17934
+ }
17935
+ for (const profileId of permissionProfileIds) {
17914
17936
  await this.ensureAssignment(
17915
17937
  user.granularId,
17916
17938
  sandbox.sandboxId,
@@ -18075,6 +18097,21 @@ var Granular = class _Granular {
18075
18097
  * List indexed sessions using ownership filters and bounded pagination.
18076
18098
  */
18077
18099
  async listSessions(options) {
18100
+ const items = [];
18101
+ let cursor = options.cursor?.trim() || void 0;
18102
+ let pageCount = 0;
18103
+ do {
18104
+ const page = await this.listSessionsPage({ ...options, cursor });
18105
+ items.push(...page.items);
18106
+ cursor = page.nextCursor || void 0;
18107
+ pageCount += 1;
18108
+ if (pageCount > 1e4) {
18109
+ throw new Error("Session pagination exceeded the safe page limit");
18110
+ }
18111
+ } while (cursor);
18112
+ return items;
18113
+ }
18114
+ async listSessionsPage(options) {
18078
18115
  const environmentId = options.environmentId?.trim();
18079
18116
  const sandboxId = options.sandboxId?.trim();
18080
18117
  const subjectId = options.subjectId?.trim();
@@ -18102,17 +18139,14 @@ var Granular = class _Granular {
18102
18139
  1,
18103
18140
  MAX_CONVERSATION_SESSION_LIST_LIMIT
18104
18141
  );
18105
- const offset = boundedSessionListInteger(
18106
- options.offset,
18107
- "offset",
18108
- 0,
18109
- 0,
18110
- MAX_CONVERSATION_SESSION_LIST_OFFSET
18111
- );
18142
+ const cursor = options.cursor?.trim();
18143
+ if (options.cursor !== void 0 && !cursor) {
18144
+ throw new Error("Session list cursor must be a non-empty string.");
18145
+ }
18112
18146
  const query = new URLSearchParams({
18113
- limit: String(limit),
18114
- offset: String(offset)
18147
+ limit: String(limit)
18115
18148
  });
18149
+ if (cursor) query.set("cursor", cursor);
18116
18150
  if (environmentId) query.set("environmentId", environmentId);
18117
18151
  if (sandboxId) query.set("sandboxId", sandboxId);
18118
18152
  if (subjectId) query.set("userId", subjectId);
@@ -18120,11 +18154,13 @@ var Granular = class _Granular {
18120
18154
  query.set("sessionScope", options.sessionScope.trim());
18121
18155
  }
18122
18156
  if (status !== "all") query.set("status", status);
18123
- const res = await this.request(
18124
- `/control/sessions?${query.toString()}`
18125
- );
18126
- const items = Array.isArray(res.items) ? res.items : [];
18127
- return items.map((row) => this.normalizeConversationSession(row));
18157
+ const res = await this.request(`/control/sessions?${query.toString()}`);
18158
+ const items = Array.isArray(res.items) ? res.items.map((row) => this.normalizeConversationSession(row)) : [];
18159
+ const nextCursor = res.nextCursor ?? null;
18160
+ if (nextCursor === cursor) {
18161
+ throw new Error("Session pagination cursor did not advance");
18162
+ }
18163
+ return { items, nextCursor };
18128
18164
  }
18129
18165
  /**
18130
18166
  * List active (open) sessions for an environment.
@@ -18151,9 +18187,6 @@ var Granular = class _Granular {
18151
18187
  if (typeof options.limit === "number") {
18152
18188
  query.set("limit", String(options.limit));
18153
18189
  }
18154
- if (typeof options.offset === "number") {
18155
- query.set("offset", String(options.offset));
18156
- }
18157
18190
  const state = await this.request(
18158
18191
  `/sdk/user-environment-state?${query.toString()}`
18159
18192
  );
@@ -18915,25 +18948,37 @@ var Granular = class _Granular {
18915
18948
  }
18916
18949
  }
18917
18950
  /**
18918
- * Ensure a permission profile exists for a sandbox, creating it if needed.
18919
- * If profileName matches an existing profile name, returns its ID.
18920
- * Otherwise, creates a v1 source-profile file shape with an allow default.
18951
+ * Resolve an existing permission profile by exact name or ID.
18952
+ *
18953
+ * `allow-all` is the sole compatibility profile that the SDK may provision
18954
+ * automatically. Every other missing reference fails closed so a typo or
18955
+ * untrusted role value cannot become an allow-default profile.
18921
18956
  */
18922
- async ensurePermissionProfile(sandboxId, profileName) {
18923
- try {
18924
- const profiles = await this.permissionProfiles.list(sandboxId);
18925
- const existing = profiles.find((p) => p.name === profileName);
18926
- if (existing) {
18927
- return existing.permissionProfileId;
18928
- }
18929
- } catch {
18957
+ async ensurePermissionProfile(sandboxId, profileReference) {
18958
+ const normalizedReference = typeof profileReference === "string" ? profileReference.trim() : "";
18959
+ if (!normalizedReference) {
18960
+ throw new Error(
18961
+ "Permission profile references must be non-empty names or IDs."
18962
+ );
18963
+ }
18964
+ const profiles = await this.permissionProfiles.list(sandboxId);
18965
+ const existing = profiles.find(
18966
+ (profile) => profile.name === normalizedReference || profile.permissionProfileId === normalizedReference
18967
+ );
18968
+ if (existing) {
18969
+ return existing.permissionProfileId;
18970
+ }
18971
+ if (normalizedReference !== "allow-all") {
18972
+ throw new Error(
18973
+ `Permission profile "${normalizedReference}" was not found in sandbox ${sandboxId}. Provision it with granular build or granular deploy before calling openEnvironment(). Only the reserved "allow-all" profile can be created automatically.`
18974
+ );
18930
18975
  }
18931
18976
  const created = await this.permissionProfiles.create(sandboxId, {
18932
- name: profileName,
18977
+ name: normalizedReference,
18933
18978
  rules: {
18934
18979
  schemaVersion: 1,
18935
- name: profileName,
18936
- description: profileName === "allow-all" ? "Every declared action is visible unless a manifest policy denies it." : `Generated permission profile ${profileName}`,
18980
+ name: normalizedReference,
18981
+ description: "Every declared action is visible unless a manifest policy denies it.",
18937
18982
  defaults: { actionPolicy: "allow" },
18938
18983
  actions: []
18939
18984
  }
@@ -19075,10 +19120,20 @@ var Granular = class _Granular {
19075
19120
  get environments() {
19076
19121
  return {
19077
19122
  list: async (sandboxId) => {
19078
- const result = await this.request(
19079
- `/control/sandboxes/${sandboxId}/environments`
19080
- );
19081
- return result.items.map(normalizeEnvironmentData);
19123
+ const environments = [];
19124
+ let cursor = null;
19125
+ do {
19126
+ const query = cursor ? `?limit=100&cursor=${encodeURIComponent(cursor)}` : "";
19127
+ const result = await this.request(
19128
+ `/control/sandboxes/${sandboxId}/environments${query}`
19129
+ );
19130
+ environments.push(...result.items.map(normalizeEnvironmentData));
19131
+ if (result.nextCursor && result.nextCursor === cursor) {
19132
+ throw new Error("Environment pagination cursor did not advance");
19133
+ }
19134
+ cursor = result.nextCursor;
19135
+ } while (cursor);
19136
+ return environments;
19082
19137
  },
19083
19138
  get: async (environmentId) => {
19084
19139
  return normalizeEnvironmentData(
@@ -19288,7 +19343,6 @@ var Granular = class _Granular {
19288
19343
  headers: {
19289
19344
  Authorization: `Bearer ${this.apiKey}`,
19290
19345
  "Content-Type": "application/json",
19291
- Connection: "close",
19292
19346
  ...options.headers
19293
19347
  }
19294
19348
  });