@granular-software/sdk 0.4.51 → 0.4.52

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.ts CHANGED
@@ -1,6 +1,6 @@
1
- export { E as Environment, a as EnvironmentSession, G as Granular, O as OntologyHandle, S as Session, W as WSClient } from './client-NSH-tpNU.js';
2
- import { E as EndpointMode, T as ToolWithHandler, a as EffectHandlerContext, M as ManifestEffectMetamodelSpec, R as ResolvedEffectBehaviors, S as SessionHeapEntry, b as SessionHeapList, c as SessionHeapSnapshot, P as Prompt, d as SessionTranscriptEntry } from './spend-BA-jZwZ0.js';
3
- export { cL as APIError, A as AccessTokenProvider, bf as ArtifactApprovalDecisionInput, bg as ArtifactApprovalDecisionResult, bd as ArtifactApprovalTask, be as ArtifactApprovalTaskListOptions, bc as ArtifactApprovalTaskStatus, a9 as Assignment, aa as AssignmentListResponse, ak as Build, am as BuildListResponse, ab as BuildPolicy, aj as BuildStatus, C as ConditionIR, J as ConnectOptions, aU as ConversationActionSuggestion, aY as ConversationAppendResult, aV as ConversationMessageAction, aX as ConversationMessageInput, aW as ConversationMessagePart, aO as ConversationMessageShowRefs, L as ConversationSessionInfo, V as ConversationSessionListOptions, Q as ConversationSessionListStatus, aP as ConversationTableCell, aQ as ConversationTableColumn, aT as ConversationTableProjection, aS as ConversationTableRow, aR as ConversationTableRowReference, af as CreateEnvironmentData, a7 as CreatePermissionProfileData, a3 as CreateSandboxData, K as CreateSessionOptions, bU as DefineRelationshipOptions, cM as DeleteResponse, D as DomainState, aD as EffectHandler, aA as EffectInfo, au as EffectInvocationMetadata, at as EffectInvocationMode, av as EffectSchema, ay as EffectVersionSelector, aw as EffectWithHandler, aC as EffectsChangedEvent, ae as EnvironmentData, aL as EnvironmentFeedbackRecord, cg as EnvironmentImporter, cf as EnvironmentImporterImportOptions, ag as EnvironmentListResponse, ca as EnvironmentRecordImportSummary, cd as EnvironmentSetupLifecycleStatus, ce as EnvironmentSetupSummary, cb as EnvironmentSetupTriggerReason, b_ as EnvironmentStateMachineProxy, bY as EnvironmentStateObservationInput, b$ as EnvironmentStateProxy, bX as EnvironmentStateTarget, bZ as EnvironmentStateUpdateInput, z as GranularAuth, y as GranularOptions, a0 as GranularQuotaPolicy, a1 as GranularQuotaProgress, G as GranularSpendContext, cK as GraphQLResult, aE as InstanceEffectHandler, I as InstanceToolHandler, aN as Job, aJ as JobFeedbackInput, aI as JobFeedbackMetadata, aK as JobFeedbackRecord, aG as JobFeedbackSentiment, aH as JobFeedbackToolCall, aF as JobStatus, aM as JobSubmitResult, ah as Manifest, cz as ManifestApprovalRequiredSpec, cJ as ManifestContent, cA as ManifestCreatesSpec, cx as ManifestDryRunSpec, cD as ManifestEffectDeclaration, cC as ManifestEffectSchema, cj as ManifestEnumRuleSpec, cF as ManifestEventStreamDef, cE as ManifestEventTypeDef, ck as ManifestFilterBySpec, cH as ManifestImport, ai as ManifestListResponse, cG as ManifestOperation, cw as ManifestPostConditionSpec, ch as ManifestPropertySpec, cB as ManifestRelationshipDef, cy as ManifestReverseSpec, cv as ManifestStateMachineSpec, cm as ManifestStateMachineStateSpec, cu as ManifestStateMachineTransitionSpec, co as ManifestStateTransitionActionSpec, cp as ManifestStateTransitionAssigneeSpec, ct as ManifestStateTransitionExpectedOutcomeSpec, cn as ManifestStateTransitionInputBinding, cs as ManifestStateTransitionPermissionSpec, cq as ManifestStateTransitionRelatedStateRequirementSpec, cr as ManifestStateTransitionRequirementsSpec, ci as ManifestValidationOperator, cl as ManifestValidationRuleSpec, cI as ManifestVolume, bo as ManualActionListOptions, bm as ManualActionOccurrence, bn as ManualActionRecordResult, bk as ManualActionRelatedRecord, bi as ManualActionSource, bh as ManualActionStatus, bp as ManualActionSuggestion, bq as ManualActionSuggestionOptions, bj as ManualActionTarget, bI as MarkUserEnvironmentReadOptions, x as MatchedPolicy, bS as ModelRef, N as NormalizedOpenAIUsage, i as OPENAI_MODEL_PRICING_USD_PER_MILLION, O as OpenAIModelPricing, h as OpenAITokenSpend, l as OpenAIUsageSpendEvent, H as OpenEnvironmentOptions, a6 as PermissionProfile, a8 as PermissionProfileListResponse, a5 as PermissionRules, u as PolicyOperator, v as PolicyOrigin, s as PolicyPredicateSource, w as PolicyRuleIR, q as PolicySource, ax as PublishEffectsResult, f as PublishToolsResult, $ as QuotaLineItemFilter, Y as QuotaPeriod, X as QuotaScopeType, Z as QuotaStatus, bM as RPCRequest, bP as RPCRequestFromServer, bN as RPCResponse, c9 as RecordImport, c8 as RecordImportItem, c6 as RecordImportItemStatus, c4 as RecordImportOptions, c7 as RecordImportStats, c5 as RecordImportStatus, c3 as RecordImportWriteMode, bl as RecordManualActionInput, by as RecordMentionInput, bV as RecordObjectOptions, c0 as RecordObjectResult, bW as RecordObjectStateValue, c1 as RecordObjectsChunkInfo, c2 as RecordObjectsOptions, m as RecordOpenAIUsageSpendOptions, o as RecordOpenAIUsageSpendResult, bx as RecordSearchOptions, bw as RecordSearchResult, B as RecordUserOptions, bT as RelationshipInfo, as as ResolvedEffectApprovalRequired, aq as ResolvedEffectDryRun, ap as ResolvedEffectPostCondition, ar as ResolvedEffectReverse, cc as RunEnvironmentImporterOptions, a2 as Sandbox, a4 as SandboxListResponse, ao as SemanticVersionDiff, an as SemanticVersionDiffEntry, bb as SessionArtifactApprovalOptions, b5 as SessionArtifactAutonomyPolicy, ba as SessionArtifactExecutionOptions, b9 as SessionArtifactExecutionResult, b4 as SessionArtifactKind, b7 as SessionArtifactListOptions, b6 as SessionArtifactRecord, b3 as SessionArtifactStatus, b8 as SessionArtifactValidationResult, bA as SessionCollectionListOptions, bC as SessionCollectionListResult, aZ as SessionConversationMessage, bz as SessionDocumentResult, b0 as SessionFileKind, b2 as SessionFileRecord, a$ as SessionFileSource, b1 as SessionFileStatus, br as SessionFileUploadOptions, bt as SessionHeapFieldType, bu as SessionHeapFieldValue, bv as SessionHeapVariable, bB as SessionJobListOptions, bs as SessionJobRecord, a_ as SessionTimelineEvent, W as SpendLineItemType, _ as SpendSummary, cN as StreamEvent, cP as StreamStats, cO as StreamSubscription, F as Subject, bO as SyncMessage, g as ToolHandler, az as ToolInfo, bQ as ToolInvokeParams, bR as ToolResultParams, e as ToolSchema, aB as ToolsChangedEvent, U as User, bE as UserEnvironmentMessagePreview, bD as UserEnvironmentPrompt, bF as UserEnvironmentSessionState, bG as UserEnvironmentState, bH as UserEnvironmentStateOptions, al as Version, ad as VersionTag, ac as VersionTracking, bL as WSClientOptions, bJ as WSDisconnectInfo, bK as WSReconnectErrorInfo, p as buildOpenAISpendEventId, k as calculateOpenAITokenSpend, j as getOpenAIModelPricing, n as normalizeOpenAIUsage, r as recordOpenAIUsageSpend, t as toGranularHttpBase } from './spend-BA-jZwZ0.js';
1
+ export { E as Environment, a as EnvironmentSession, G as Granular, O as OntologyHandle, S as Session, W as WSClient } from './client-BMfjRJTs.js';
2
+ import { E as EndpointMode, T as ToolWithHandler, a as EffectHandlerContext, M as ManifestEffectMetamodelSpec, R as ResolvedEffectBehaviors, S as SessionHeapEntry, b as SessionHeapList, c as SessionHeapSnapshot, P as Prompt, d as SessionTranscriptEntry } from './spend-DpRRCrAr.js';
3
+ export { cN as APIError, A as AccessTokenProvider, J as AdoptEnvironmentOptions, bg as ArtifactApprovalDecisionInput, bh as ArtifactApprovalDecisionResult, be as ArtifactApprovalTask, bf as ArtifactApprovalTaskListOptions, bd as ArtifactApprovalTaskStatus, aa as Assignment, ab as AssignmentListResponse, al as Build, an as BuildListResponse, ac as BuildPolicy, ak as BuildStatus, C as ConditionIR, K as ConnectOptions, aV as ConversationActionSuggestion, aZ as ConversationAppendResult, aW as ConversationMessageAction, aY as ConversationMessageInput, aX as ConversationMessagePart, aP as ConversationMessageShowRefs, Q as ConversationSessionInfo, W as ConversationSessionListOptions, V as ConversationSessionListStatus, aQ as ConversationTableCell, aR as ConversationTableColumn, aU as ConversationTableProjection, aT as ConversationTableRow, aS as ConversationTableRowReference, ag as CreateEnvironmentData, a8 as CreatePermissionProfileData, a4 as CreateSandboxData, L as CreateSessionOptions, bV as DefineRelationshipOptions, cO as DeleteResponse, D as DomainState, aE as EffectHandler, aB as EffectInfo, av as EffectInvocationMetadata, au as EffectInvocationMode, aw as EffectSchema, az as EffectVersionSelector, ax as EffectWithHandler, aD as EffectsChangedEvent, af as EnvironmentData, aM as EnvironmentFeedbackRecord, ci as EnvironmentImporter, ch as EnvironmentImporterImportOptions, ah as EnvironmentListResponse, cb as EnvironmentRecordImportSummary, cg as EnvironmentSetupImporterClaim, ce as EnvironmentSetupLifecycleStatus, cf as EnvironmentSetupSummary, cc as EnvironmentSetupTriggerReason, b$ as EnvironmentStateMachineProxy, bZ as EnvironmentStateObservationInput, c0 as EnvironmentStateProxy, bY as EnvironmentStateTarget, b_ as EnvironmentStateUpdateInput, z as GranularAuth, y as GranularOptions, a1 as GranularQuotaPolicy, a2 as GranularQuotaProgress, G as GranularSpendContext, cM as GraphQLResult, aF as InstanceEffectHandler, I as InstanceToolHandler, aO as Job, aK as JobFeedbackInput, aJ as JobFeedbackMetadata, aL as JobFeedbackRecord, aH as JobFeedbackSentiment, aI as JobFeedbackToolCall, aG as JobStatus, aN as JobSubmitResult, ai as Manifest, cB as ManifestApprovalRequiredSpec, cL as ManifestContent, cC as ManifestCreatesSpec, cz as ManifestDryRunSpec, cF as ManifestEffectDeclaration, cE as ManifestEffectSchema, cl as ManifestEnumRuleSpec, cH as ManifestEventStreamDef, cG as ManifestEventTypeDef, cm as ManifestFilterBySpec, cJ as ManifestImport, aj as ManifestListResponse, cI as ManifestOperation, cy as ManifestPostConditionSpec, cj as ManifestPropertySpec, cD as ManifestRelationshipDef, cA as ManifestReverseSpec, cx as ManifestStateMachineSpec, co as ManifestStateMachineStateSpec, cw as ManifestStateMachineTransitionSpec, cq as ManifestStateTransitionActionSpec, cr as ManifestStateTransitionAssigneeSpec, cv as ManifestStateTransitionExpectedOutcomeSpec, cp as ManifestStateTransitionInputBinding, cu as ManifestStateTransitionPermissionSpec, cs as ManifestStateTransitionRelatedStateRequirementSpec, ct as ManifestStateTransitionRequirementsSpec, ck as ManifestValidationOperator, cn as ManifestValidationRuleSpec, cK as ManifestVolume, bp as ManualActionListOptions, bn as ManualActionOccurrence, bo as ManualActionRecordResult, bl as ManualActionRelatedRecord, bj as ManualActionSource, bi as ManualActionStatus, bq as ManualActionSuggestion, br as ManualActionSuggestionOptions, bk as ManualActionTarget, bJ as MarkUserEnvironmentReadOptions, x as MatchedPolicy, bT as ModelRef, N as NormalizedOpenAIUsage, i as OPENAI_MODEL_PRICING_USD_PER_MILLION, O as OpenAIModelPricing, h as OpenAITokenSpend, l as OpenAIUsageSpendEvent, H as OpenEnvironmentOptions, a7 as PermissionProfile, a9 as PermissionProfileListResponse, a6 as PermissionRules, u as PolicyOperator, v as PolicyOrigin, s as PolicyPredicateSource, w as PolicyRuleIR, q as PolicySource, ay as PublishEffectsResult, f as PublishToolsResult, a0 as QuotaLineItemFilter, Z as QuotaPeriod, Y as QuotaScopeType, _ as QuotaStatus, bN as RPCRequest, bQ as RPCRequestFromServer, bO as RPCResponse, ca as RecordImport, c9 as RecordImportItem, c7 as RecordImportItemStatus, c5 as RecordImportOptions, c8 as RecordImportStats, c6 as RecordImportStatus, c4 as RecordImportWriteMode, bm as RecordManualActionInput, bz as RecordMentionInput, bW as RecordObjectOptions, c1 as RecordObjectResult, bX as RecordObjectStateValue, c2 as RecordObjectsChunkInfo, c3 as RecordObjectsOptions, m as RecordOpenAIUsageSpendOptions, o as RecordOpenAIUsageSpendResult, by as RecordSearchOptions, bx as RecordSearchResult, B as RecordUserOptions, bU as RelationshipInfo, at as ResolvedEffectApprovalRequired, ar as ResolvedEffectDryRun, aq as ResolvedEffectPostCondition, as as ResolvedEffectReverse, cd as RunEnvironmentImporterOptions, a3 as Sandbox, a5 as SandboxListResponse, ap as SemanticVersionDiff, ao as SemanticVersionDiffEntry, bc as SessionArtifactApprovalOptions, b6 as SessionArtifactAutonomyPolicy, bb as SessionArtifactExecutionOptions, ba as SessionArtifactExecutionResult, b5 as SessionArtifactKind, b8 as SessionArtifactListOptions, b7 as SessionArtifactRecord, b4 as SessionArtifactStatus, b9 as SessionArtifactValidationResult, bB as SessionCollectionListOptions, bD as SessionCollectionListResult, a_ as SessionConversationMessage, bA as SessionDocumentResult, b1 as SessionFileKind, b3 as SessionFileRecord, b0 as SessionFileSource, b2 as SessionFileStatus, bs as SessionFileUploadOptions, bu as SessionHeapFieldType, bv as SessionHeapFieldValue, bw as SessionHeapVariable, bC as SessionJobListOptions, bt as SessionJobRecord, a$ as SessionTimelineEvent, X as SpendLineItemType, $ as SpendSummary, cP as StreamEvent, cR as StreamStats, cQ as StreamSubscription, F as Subject, bP as SyncMessage, g as ToolHandler, aA as ToolInfo, bR as ToolInvokeParams, bS as ToolResultParams, e as ToolSchema, aC as ToolsChangedEvent, U as User, bF as UserEnvironmentMessagePreview, bE as UserEnvironmentPrompt, bG as UserEnvironmentSessionState, bH as UserEnvironmentState, bI as UserEnvironmentStateOptions, am as Version, ae as VersionTag, ad as VersionTracking, bM as WSClientOptions, bK as WSDisconnectInfo, bL as WSReconnectErrorInfo, p as buildOpenAISpendEventId, k as calculateOpenAITokenSpend, j as getOpenAIModelPricing, n as normalizeOpenAIUsage, r as recordOpenAIUsageSpend, t as toGranularHttpBase } from './spend-DpRRCrAr.js';
4
4
  export { BuildGranularAgentSystemPromptInput, GeneratedJobCodeIssue, GranularAgentExecutionCheckpoint, GranularAgentHeapSummaryOptions, GranularAgentManualActionMemorySuggestion, GranularAgentPromptCapabilities, GranularAgentReferentFocus, GranularAgentSessionContext, GranularAgentToolInfo, GranularAgentWorkflowFocus, GranularReasoningTraceChunkResult, GranularReasoningTraceOptions, HarnessContinuationDecision, HarnessControllerBudgets, HarnessProjectionOptions, HarnessPromptLike, HarnessRenderedContinuation, HarnessRenderedPrompt, HarnessTemplate, HarnessTemplateManifest, HarnessTemplateSelectionOptions, HarnessTemplateStatus, HarnessVerifierSnapshot, HarnessVerifierSnapshotInput, ReviewGeneratedJobCodeOptions, buildContinuationInstruction, buildContinuationInstructionFromTemplate, buildGranularAgentCheckpointBlock, buildGranularAgentDomainBlock, buildGranularAgentFileBlock, buildGranularAgentHeapBlock, buildGranularAgentLoopBlock, buildGranularAgentManualActionBlock, buildGranularAgentManualActionMemorySummary, buildGranularAgentReferentBlock, buildGranularAgentRuntimeImportsBlock, buildGranularAgentSessionBlock, buildGranularAgentSystemPrompt, buildGranularAgentSystemPromptFromTemplate, buildGranularAgentToolBlock, buildGranularAgentWorkflowBlock, consumeGranularReasoningOnlyChunk, consumeGranularReasoningTraceChunk, createHarnessVerifierSnapshot, evaluateContinuation, getCurrentClosureId, getDefaultHarnessTemplateId, getExclusivePromptTarget, hasOpenPrompt, hashHarnessTemplateValue, listHarnessTemplates, projectConversationReferentFocus, projectConversationReferentSummary, projectHeapSummary, projectLoopSummary, projectSessionFileSummary, projectWorkflowFocus, projectWorkflowSummary, renderContinuationInstructionFromTemplate, renderGranularAgentSystemPromptFromTemplate, resolveHarnessTemplate, reviewGeneratedJobCode, stripGranularReasoningTrace, validateHarnessTemplateManifest } from './agent-harness.js';
5
5
  import '@automerge/automerge';
6
6
  import '@automerge/automerge/slim';
package/dist/index.js CHANGED
@@ -14309,7 +14309,8 @@ function normalizeEnvironmentSetupSummary(setup) {
14309
14309
  environmentId: String(setup.environmentId || ""),
14310
14310
  sandboxId: String(setup.sandboxId || ""),
14311
14311
  subjectId: String(setup.subjectId || ""),
14312
- triggerReason: setup.triggerReason === "fresh_after_version_update" ? "fresh_after_version_update" : "new_environment",
14312
+ triggerReason: setup.triggerReason === "fresh_after_version_update" ? "fresh_after_version_update" : setup.triggerReason === "explicit_reset" ? "explicit_reset" : "new_environment",
14313
+ operationKey: typeof setup.operationKey === "string" ? setup.operationKey : null,
14313
14314
  lifecycleStatus: setup.lifecycleStatus === "completed" || setup.lifecycleStatus === "failed" ? setup.lifecycleStatus : "running",
14314
14315
  stage: typeof setup.stage === "string" ? setup.stage : null,
14315
14316
  totalObjectsToImport: Number(setup.totalObjectsToImport || 0),
@@ -15591,6 +15592,7 @@ var Environment = class _Environment {
15591
15592
  records: recordsToImport,
15592
15593
  batchSize: options.batchSize,
15593
15594
  setupRunId: options.setupRunId,
15595
+ operationKey: options.operationKey,
15594
15596
  writeMode: options.writeMode
15595
15597
  })
15596
15598
  }
@@ -16395,6 +16397,107 @@ var Granular = class _Granular {
16395
16397
  await this.maybeRunEnvironmentImporter(resolved, environment);
16396
16398
  return environment;
16397
16399
  }
16400
+ /**
16401
+ * Read the active environment selected by Granular for an already-recorded
16402
+ * external user. This is intentionally read-only: browser/login code must
16403
+ * not create subjects or environments as a side effect.
16404
+ */
16405
+ async getActiveEnvironmentForUser(options) {
16406
+ const sandboxId = options.sandboxId.trim();
16407
+ const tagName = options.tag.trim();
16408
+ const userId = options.userId.trim();
16409
+ if (!sandboxId || !tagName || !userId) {
16410
+ throw new Error(
16411
+ "getActiveEnvironmentForUser() requires sandboxId, tag, and userId."
16412
+ );
16413
+ }
16414
+ const subjects = await this.request(
16415
+ `/control/subjects?identityId=${encodeURIComponent(userId)}`
16416
+ );
16417
+ const subject = (subjects.items || []).find(
16418
+ (item) => item.identityId === userId || item.userId === userId
16419
+ );
16420
+ if (!subject?.subjectId && !subject?.granularId) {
16421
+ return null;
16422
+ }
16423
+ const subjectId = subject.subjectId || subject.granularId;
16424
+ const tags = await this.request(
16425
+ `/control/sandboxes/${encodeURIComponent(sandboxId)}/tags`
16426
+ );
16427
+ const tag = (tags.items || []).find(
16428
+ (item) => item?.name === tagName
16429
+ );
16430
+ if (!tag) return null;
16431
+ const query = new URLSearchParams({
16432
+ tagId: tag.tagId,
16433
+ slot: options.slot?.trim() || "default"
16434
+ });
16435
+ try {
16436
+ const payload = await this.request(
16437
+ `/control/sandboxes/${encodeURIComponent(sandboxId)}/subjects/${encodeURIComponent(subjectId)}/active-environment?${query.toString()}`
16438
+ );
16439
+ return payload.environment ? this.bindEnvironmentHandle(
16440
+ normalizeEnvironmentData(payload.environment)
16441
+ ) : null;
16442
+ } catch (error) {
16443
+ const message = error instanceof Error ? error.message : String(error);
16444
+ if (message.includes("404") || message.includes("not found")) {
16445
+ return null;
16446
+ }
16447
+ throw error;
16448
+ }
16449
+ }
16450
+ /**
16451
+ * Register one reviewed, pre-existing environment as the active workspace
16452
+ * for an external user. This is for a controlled migration only: it does
16453
+ * not create an environment and it does not run an importer.
16454
+ */
16455
+ async adoptEnvironmentForUser(options) {
16456
+ const sandboxId = options.sandboxId.trim();
16457
+ const tagName = options.tag.trim();
16458
+ const userId = options.userId.trim();
16459
+ const environmentId = options.environmentId.trim();
16460
+ if (!sandboxId || !tagName || !userId || !environmentId) {
16461
+ throw new Error(
16462
+ "adoptEnvironmentForUser() requires sandboxId, tag, userId, and environmentId."
16463
+ );
16464
+ }
16465
+ const subjects = await this.request(
16466
+ `/control/subjects?identityId=${encodeURIComponent(userId)}`
16467
+ );
16468
+ const subject = (subjects.items || []).find(
16469
+ (item) => item.identityId === userId || item.userId === userId
16470
+ );
16471
+ if (!subject?.subjectId && !subject?.granularId) {
16472
+ throw new Error(`No Granular subject exists for user ${userId}.`);
16473
+ }
16474
+ const tags = await this.request(`/control/sandboxes/${encodeURIComponent(sandboxId)}/tags`);
16475
+ const tag = (tags.items || []).find(
16476
+ (item) => item?.name === tagName
16477
+ );
16478
+ if (!tag) {
16479
+ throw new Error(`Tag ${tagName} was not found in sandbox ${sandboxId}.`);
16480
+ }
16481
+ const payload = await this.request(
16482
+ "/control/environment-activations/adopt",
16483
+ {
16484
+ method: "POST",
16485
+ body: JSON.stringify({
16486
+ environmentId,
16487
+ subjectId: subject.subjectId || subject.granularId,
16488
+ tagId: tag.tagId,
16489
+ slot: options.slot?.trim() || "default",
16490
+ confirmExistingData: true
16491
+ })
16492
+ }
16493
+ );
16494
+ if (!payload.environment) {
16495
+ throw new Error("Granular did not return the adopted environment.");
16496
+ }
16497
+ return this.bindEnvironmentHandle(
16498
+ normalizeEnvironmentData(payload.environment)
16499
+ );
16500
+ }
16398
16501
  /**
16399
16502
  * Deprecated compatibility alias for `openEnvironment()`.
16400
16503
  *
@@ -16426,7 +16529,9 @@ var Granular = class _Granular {
16426
16529
  requestedOntology,
16427
16530
  sandboxId: environmentData.sandboxId,
16428
16531
  subjectId: environmentData.subjectId,
16429
- setupTriggerReason: options.reason || "new_environment"
16532
+ externalUserId: environmentData.subjectId,
16533
+ setupTriggerReason: options.reason || "new_environment",
16534
+ setupOperationKey: options.operationKey
16430
16535
  },
16431
16536
  environment
16432
16537
  );
@@ -16438,20 +16543,17 @@ var Granular = class _Granular {
16438
16543
  }
16439
16544
  return tag;
16440
16545
  }
16441
- buildManagedEnvironmentName(tag, versionId) {
16442
- return `__sdk__${tag}__${versionId}__pinned`;
16546
+ buildManagedEnvironmentName(tag, versionId, resetKey) {
16547
+ if (!resetKey) {
16548
+ return `__sdk__${tag}__${versionId}__tracked`;
16549
+ }
16550
+ const safeResetKey = resetKey.replace(/[^a-zA-Z0-9_-]/g, "_").slice(0, 80);
16551
+ return `__sdk__${tag}__${versionId}__reset__${safeResetKey}`;
16443
16552
  }
16444
16553
  isManagedEnvironmentName(environment, tagName) {
16445
16554
  const name = environment.environment || environment.envName || "";
16446
16555
  return name.startsWith(`__sdk__${tagName}__`);
16447
16556
  }
16448
- isPinnedToVersion(environment, versionId) {
16449
- return environment.buildPolicy.mode === "pinned" && (environment.versionId === versionId || environment.buildPolicy.versionId === versionId || environment.buildPolicy.buildId === versionId);
16450
- }
16451
- matchesTagTrackedEnvironment(environment, tagName, tagId) {
16452
- const environmentTagName = environment.tag?.name || environment.buildPolicy.tagName || null;
16453
- return environment.tagId === tagId || environmentTagName === tagName || environment.environment === tagName || environment.envName === tagName || environment.environment === this.buildManagedEnvironmentName(tagName, environment.versionId) || environment.envName === this.buildManagedEnvironmentName(tagName, environment.versionId);
16454
- }
16455
16557
  sortEnvironmentsByRecency(environments) {
16456
16558
  return [...environments].sort(
16457
16559
  (left, right) => right.updatedAt - left.updatedAt
@@ -16501,48 +16603,119 @@ var Granular = class _Granular {
16501
16603
  `Tag "${tagName}" does not currently point to a build/version.`
16502
16604
  );
16503
16605
  }
16606
+ const slot = options.slot?.trim() || "default";
16607
+ const resetKey = options.resetKey?.trim() || void 0;
16608
+ const resolveActive = async (operationKey) => {
16609
+ const query = new URLSearchParams({ tagId: tag.tagId, slot });
16610
+ if (operationKey) query.set("operationKey", operationKey);
16611
+ try {
16612
+ const payload = await this.request(
16613
+ `/control/sandboxes/${encodeURIComponent(sandbox.sandboxId)}/subjects/${encodeURIComponent(user.granularId)}/active-environment?${query.toString()}`
16614
+ );
16615
+ return payload.environment ? normalizeEnvironmentData(payload.environment) : null;
16616
+ } catch (error) {
16617
+ const message = error instanceof Error ? error.message : String(error);
16618
+ if (message.includes("404") || message.includes("not found")) {
16619
+ return null;
16620
+ }
16621
+ throw error;
16622
+ }
16623
+ };
16624
+ const activate = async (environment2) => {
16625
+ const payload = await this.request(
16626
+ "/control/environment-activations",
16627
+ {
16628
+ method: "POST",
16629
+ body: JSON.stringify({
16630
+ environmentId: environment2.environmentId,
16631
+ tagId: tag.tagId,
16632
+ slot,
16633
+ operationKey: resetKey,
16634
+ operation: resetKey ? "explicit_reset" : void 0
16635
+ })
16636
+ }
16637
+ );
16638
+ return normalizeEnvironmentData(payload.environment);
16639
+ };
16640
+ if (resetKey) {
16641
+ const resetEnvironment = await resolveActive(resetKey);
16642
+ if (resetEnvironment) {
16643
+ return {
16644
+ environment: resetEnvironment,
16645
+ requestedOntology: ontology,
16646
+ sandboxId: sandbox.sandboxId,
16647
+ subjectId: user.granularId,
16648
+ externalUserId: user.userId,
16649
+ // Retrying an explicit reset must also resume its durable setup run.
16650
+ // Otherwise a Container crash after queue submission would leave a
16651
+ // valid environment permanently marked as "running".
16652
+ setupTriggerReason: "explicit_reset",
16653
+ setupOperationKey: resetKey
16654
+ };
16655
+ }
16656
+ } else {
16657
+ const active = await resolveActive();
16658
+ if (active && (active.versionId === targetVersionId || options.createFreshIfOutdated !== true)) {
16659
+ return {
16660
+ environment: active,
16661
+ requestedOntology: ontology,
16662
+ sandboxId: sandbox.sandboxId,
16663
+ subjectId: user.granularId,
16664
+ externalUserId: user.userId
16665
+ };
16666
+ }
16667
+ }
16504
16668
  const allEnvironments = await this.environments.list(sandbox.sandboxId);
16505
16669
  const userEnvironments = allEnvironments.filter(
16506
- (environment) => environment.subjectId === user.granularId
16670
+ (environment2) => environment2.subjectId === user.granularId
16507
16671
  );
16508
16672
  const currentMatches = this.sortEnvironmentsByRecency(
16509
16673
  userEnvironments.filter(
16510
- (environment) => this.matchesTagTrackedEnvironment(environment, tagName, tag.tagId) && environment.versionId === targetVersionId && (!this.isManagedEnvironmentName(environment, tagName) || this.isPinnedToVersion(environment, targetVersionId))
16674
+ (environment2) => environment2.tagId === tag.tagId && environment2.versionId === targetVersionId
16511
16675
  )
16512
16676
  );
16513
- if (currentMatches.length > 0) {
16677
+ if (!resetKey && currentMatches.length > 0) {
16678
+ const environment2 = await activate(currentMatches[0]);
16514
16679
  return {
16515
- environment: currentMatches[0],
16680
+ environment: environment2,
16516
16681
  requestedOntology: ontology,
16517
16682
  sandboxId: sandbox.sandboxId,
16518
- subjectId: user.granularId
16683
+ subjectId: user.granularId,
16684
+ externalUserId: user.userId
16519
16685
  };
16520
16686
  }
16521
16687
  const outdatedMatches = this.sortEnvironmentsByRecency(
16522
- userEnvironments.filter(
16523
- (environment) => this.matchesTagTrackedEnvironment(environment, tagName, tag.tagId)
16524
- )
16688
+ userEnvironments.filter((environment2) => environment2.tagId === tag.tagId)
16525
16689
  );
16526
16690
  if (outdatedMatches.length > 0 && options.createFreshIfOutdated !== true) {
16691
+ const environment2 = await activate(outdatedMatches[0]);
16527
16692
  return {
16528
- environment: outdatedMatches[0],
16693
+ environment: environment2,
16529
16694
  requestedOntology: ontology,
16530
16695
  sandboxId: sandbox.sandboxId,
16531
- subjectId: user.granularId
16696
+ subjectId: user.granularId,
16697
+ externalUserId: user.userId
16532
16698
  };
16533
16699
  }
16700
+ const created = await this.environments.create(sandbox.sandboxId, {
16701
+ subjectId: user.granularId,
16702
+ environment: this.buildManagedEnvironmentName(
16703
+ tagName,
16704
+ targetVersionId,
16705
+ resetKey
16706
+ ),
16707
+ tagId: tag.tagId,
16708
+ permissionProfileId: null
16709
+ });
16710
+ const environment = await activate(created);
16534
16711
  return {
16535
- environment: await this.environments.create(sandbox.sandboxId, {
16536
- subjectId: user.granularId,
16537
- environment: this.buildManagedEnvironmentName(tagName, targetVersionId),
16538
- tagId: tag.tagId,
16539
- versionId: targetVersionId,
16540
- permissionProfileId: null
16541
- }),
16712
+ environment,
16542
16713
  requestedOntology: ontology,
16543
16714
  sandboxId: sandbox.sandboxId,
16544
16715
  subjectId: user.granularId,
16545
- setupTriggerReason: outdatedMatches.length > 0 ? "fresh_after_version_update" : "new_environment"
16716
+ externalUserId: user.userId,
16717
+ setupTriggerReason: resetKey ? "explicit_reset" : outdatedMatches.length > 0 ? "fresh_after_version_update" : "new_environment",
16718
+ setupOperationKey: resetKey
16546
16719
  };
16547
16720
  }
16548
16721
  /**
@@ -16821,11 +16994,24 @@ var Granular = class _Granular {
16821
16994
  {
16822
16995
  method: "POST",
16823
16996
  body: JSON.stringify({
16824
- triggerReason: resolved.setupTriggerReason
16997
+ triggerReason: resolved.setupTriggerReason,
16998
+ operationKey: resolved.setupOperationKey
16825
16999
  })
16826
17000
  }
16827
17001
  );
16828
17002
  const setupRunId = setupRun.setupRunId;
17003
+ let claim = null;
17004
+ for (let attempt = 0; attempt < 3; attempt += 1) {
17005
+ claim = await this.request(
17006
+ `/control/environment-setup-runs/${setupRunId}/importer-claim`,
17007
+ { method: "POST", body: JSON.stringify({}) }
17008
+ );
17009
+ if (claim.action !== "busy") break;
17010
+ await sleep(Math.min(3e4, Math.max(250, claim.retryAfterMs || 1e3)));
17011
+ }
17012
+ if (!claim) {
17013
+ throw new Error(`Unable to claim environment setup run ${setupRunId}.`);
17014
+ }
16829
17015
  const updateSetupRun = async (patch) => {
16830
17016
  await this.request(
16831
17017
  `/control/environment-setup-runs/${setupRunId}`,
@@ -16835,10 +17021,26 @@ var Granular = class _Granular {
16835
17021
  }
16836
17022
  );
16837
17023
  };
17024
+ if (claim.action === "submitted") {
17025
+ const completedSetupRun = await this.request(
17026
+ `/control/environment-setup-runs/${setupRunId}`,
17027
+ { method: "PATCH", body: JSON.stringify({ markHookCompleted: true }) }
17028
+ );
17029
+ const refreshedEnvironment = await this.environments.get(
17030
+ environment.environmentId
17031
+ );
17032
+ environment.syncEnvironmentData(refreshedEnvironment);
17033
+ return completedSetupRun;
17034
+ }
17035
+ if (claim.action === "busy" || claim.action === "terminal") {
17036
+ return claim.summary;
17037
+ }
17038
+ let importSequence = 0;
16838
17039
  const importerContext = {
16839
17040
  environmentId: environment.environmentId,
16840
17041
  sandboxId: environment.sandboxId,
16841
17042
  subjectId: environment.subjectId,
17043
+ externalUserId: resolved.externalUserId,
16842
17044
  reason: resolved.setupTriggerReason,
16843
17045
  incrementTotalObjectsToImportCount: async (n) => {
16844
17046
  const safeIncrement = Math.max(0, Math.trunc(n));
@@ -16855,7 +17057,10 @@ var Granular = class _Granular {
16855
17057
  importRecords: async (records, options) => environment.enqueueRecordImport(records, {
16856
17058
  batchSize: options?.batchSize,
16857
17059
  writeMode: options?.writeMode,
16858
- setupRunId
17060
+ setupRunId,
17061
+ // Sequence is deterministic for a retry of one importer hook. It
17062
+ // prevents a Container restart from creating a second queue import.
17063
+ operationKey: `${setupRunId}:import:${importSequence++}`
16859
17064
  })
16860
17065
  };
16861
17066
  try {