@stigmer/sdk 3.1.7 → 3.1.9

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.
Files changed (58) hide show
  1. package/__tests__/errors.test.js +42 -0
  2. package/__tests__/errors.test.js.map +1 -1
  3. package/__tests__/guest-auth.test.d.ts +2 -0
  4. package/__tests__/guest-auth.test.d.ts.map +1 -0
  5. package/__tests__/guest-auth.test.js +223 -0
  6. package/__tests__/guest-auth.test.js.map +1 -0
  7. package/__tests__/sharing.test.d.ts +2 -0
  8. package/__tests__/sharing.test.d.ts.map +1 -0
  9. package/__tests__/sharing.test.js +106 -0
  10. package/__tests__/sharing.test.js.map +1 -0
  11. package/gen/agentshare.d.ts +45 -0
  12. package/gen/agentshare.d.ts.map +1 -0
  13. package/gen/agentshare.js +142 -0
  14. package/gen/agentshare.js.map +1 -0
  15. package/gen/authorization-config.d.ts.map +1 -1
  16. package/gen/authorization-config.js +1 -0
  17. package/gen/authorization-config.js.map +1 -1
  18. package/gen/client.d.ts +4 -0
  19. package/gen/client.d.ts.map +1 -1
  20. package/gen/client.js +4 -0
  21. package/gen/client.js.map +1 -1
  22. package/gen/environment.d.ts +2 -0
  23. package/gen/environment.d.ts.map +1 -1
  24. package/gen/environment.js +8 -0
  25. package/gen/environment.js.map +1 -1
  26. package/gen/platformclient.d.ts +2 -1
  27. package/gen/platformclient.d.ts.map +1 -1
  28. package/gen/platformclient.js +8 -0
  29. package/gen/platformclient.js.map +1 -1
  30. package/gen/session.d.ts +2 -2
  31. package/gen/session.d.ts.map +1 -1
  32. package/gen/session.js +2 -2
  33. package/gen/session.js.map +1 -1
  34. package/guest-auth.d.ts +175 -0
  35. package/guest-auth.d.ts.map +1 -0
  36. package/guest-auth.js +227 -0
  37. package/guest-auth.js.map +1 -0
  38. package/index.d.ts +3 -0
  39. package/index.d.ts.map +1 -1
  40. package/index.js +5 -0
  41. package/index.js.map +1 -1
  42. package/package.json +2 -2
  43. package/sharing.d.ts +76 -0
  44. package/sharing.d.ts.map +1 -0
  45. package/sharing.js +117 -0
  46. package/sharing.js.map +1 -0
  47. package/src/__tests__/errors.test.ts +56 -0
  48. package/src/__tests__/guest-auth.test.ts +287 -0
  49. package/src/__tests__/sharing.test.ts +159 -0
  50. package/src/gen/agentshare.ts +148 -0
  51. package/src/gen/authorization-config.ts +1 -0
  52. package/src/gen/client.ts +5 -0
  53. package/src/gen/environment.ts +7 -1
  54. package/src/gen/platformclient.ts +7 -1
  55. package/src/gen/session.ts +3 -3
  56. package/src/guest-auth.ts +334 -0
  57. package/src/index.ts +25 -0
  58. package/src/sharing.ts +135 -0
@@ -0,0 +1,148 @@
1
+ // Code generated by stigmer-codegen. DO NOT EDIT.
2
+
3
+ import { wrapError } from "./errors.js";
4
+ import { stripUndefined } from "./proto-utils.js";
5
+ import { type ResourceRef } from "./types.js";
6
+ import { create } from "@bufbuild/protobuf";
7
+ import { createClient, type Client, type Transport } from "@connectrpc/connect";
8
+ import { AgentShareSchema, type AgentShare } from "@stigmer/protos/ai/stigmer/agentic/agentshare/v1/api_pb";
9
+ import { AgentShareCommandController } from "@stigmer/protos/ai/stigmer/agentic/agentshare/v1/command_pb";
10
+ import { AgentShareIdSchema, RotateShareLinkInputSchema, GetAgentSharesByAgentRequestSchema, AgentShareListSchema, ListAgentSharesRequestSchema, GetSharedProfileRequestSchema, SharedAgentProfileSchema, type RotateShareLinkInput, type GetAgentSharesByAgentRequest, type AgentShareList, type ListAgentSharesRequest, type GetSharedProfileRequest, type SharedAgentProfile } from "@stigmer/protos/ai/stigmer/agentic/agentshare/v1/io_pb";
11
+ import { AgentShareQueryController } from "@stigmer/protos/ai/stigmer/agentic/agentshare/v1/query_pb";
12
+ import { AgentShareSpecSchema, AgentShareAudience, AgentShareMessagesSchema } from "@stigmer/protos/ai/stigmer/agentic/agentshare/v1/spec_pb";
13
+ import { ApiResourceKind } from "@stigmer/protos/ai/stigmer/commons/apiresource/apiresourcekind/api_resource_kind_pb";
14
+ import { ApiResourceVisibility } from "@stigmer/protos/ai/stigmer/commons/apiresource/enum_pb";
15
+ import { ApiResourceReferenceSchema } from "@stigmer/protos/ai/stigmer/commons/apiresource/io_pb";
16
+ import { ApiResourceMetadataSchema } from "@stigmer/protos/ai/stigmer/commons/apiresource/metadata_pb";
17
+
18
+ /** Provides operations on agentshare resources. */
19
+ export class AgentShareClient {
20
+ private readonly command: Client<typeof AgentShareCommandController>;
21
+ private readonly query: Client<typeof AgentShareQueryController>;
22
+
23
+ constructor(transport: Transport) {
24
+ this.command = createClient(AgentShareCommandController, transport);
25
+ this.query = createClient(AgentShareQueryController, transport);
26
+ }
27
+
28
+ async apply(input: AgentShareInput): Promise<AgentShare> {
29
+ try {
30
+ return await this.command.apply(buildAgentShareProto(input));
31
+ } catch (e) { throw wrapError(e); }
32
+ }
33
+
34
+ async create(input: AgentShareInput): Promise<AgentShare> {
35
+ try {
36
+ return await this.command.create(buildAgentShareProto(input));
37
+ } catch (e) { throw wrapError(e); }
38
+ }
39
+
40
+ async update(input: AgentShareInput): Promise<AgentShare> {
41
+ try {
42
+ return await this.command.update(buildAgentShareProto(input));
43
+ } catch (e) { throw wrapError(e); }
44
+ }
45
+
46
+ async rotateShareLink(input: RotateShareLinkInput): Promise<AgentShare> {
47
+ try {
48
+ return await this.command.rotateShareLink(input);
49
+ } catch (e) { throw wrapError(e); }
50
+ }
51
+
52
+ async delete(id: string): Promise<AgentShare> {
53
+ try {
54
+ return await this.command.delete(create(AgentShareIdSchema, { value: id }));
55
+ } catch (e) { throw wrapError(e); }
56
+ }
57
+
58
+ async get(id: string): Promise<AgentShare> {
59
+ try {
60
+ return await this.query.get(create(AgentShareIdSchema, { value: id }));
61
+ } catch (e) { throw wrapError(e); }
62
+ }
63
+
64
+ async getByReference(ref: ResourceRef): Promise<AgentShare> {
65
+ try {
66
+ return await this.query.getByReference(create(ApiResourceReferenceSchema, { ...ref, kind: ApiResourceKind.agent_share }));
67
+ } catch (e) { throw wrapError(e); }
68
+ }
69
+
70
+ async getByAgent(input: GetAgentSharesByAgentRequest): Promise<AgentShareList> {
71
+ try {
72
+ return await this.query.getByAgent(input);
73
+ } catch (e) { throw wrapError(e); }
74
+ }
75
+
76
+ async list(input: ListAgentSharesRequest): Promise<AgentShareList> {
77
+ try {
78
+ return await this.query.list(input);
79
+ } catch (e) { throw wrapError(e); }
80
+ }
81
+
82
+ async getSharedProfile(input: GetSharedProfileRequest): Promise<SharedAgentProfile> {
83
+ try {
84
+ return await this.query.getSharedProfile(input);
85
+ } catch (e) { throw wrapError(e); }
86
+ }
87
+
88
+ async getSharedProfileForMember(ref: ResourceRef): Promise<SharedAgentProfile> {
89
+ try {
90
+ return await this.query.getSharedProfileForMember(create(ApiResourceReferenceSchema, { ...ref, kind: ApiResourceKind.agent_share }));
91
+ } catch (e) { throw wrapError(e); }
92
+ }
93
+ }
94
+
95
+ /** Input for creating/updating a AgentShare. */
96
+ export interface AgentShareInput {
97
+ name: string;
98
+ slug?: string;
99
+ org: string;
100
+ labels?: Record<string, string>;
101
+ visibility?: ApiResourceVisibility;
102
+ agentRef: ResourceRef;
103
+ enabled?: boolean;
104
+ audience?: AgentShareAudience;
105
+ allowedOrigins?: string[];
106
+ messages?: AgentShareMessagesInput;
107
+ environmentRefs?: ResourceRef[];
108
+ }
109
+
110
+ /** SDK input type for AgentShareMessages. */
111
+ export interface AgentShareMessagesInput {
112
+ rateLimited?: string;
113
+ unavailable?: string;
114
+ conversationEnded?: string;
115
+ }
116
+
117
+ function buildAgentShareMessagesProto(input: AgentShareMessagesInput) {
118
+ return Object.assign(create(AgentShareMessagesSchema), stripUndefined({
119
+ rateLimited: input.rateLimited,
120
+ unavailable: input.unavailable,
121
+ conversationEnded: input.conversationEnded,
122
+ }));
123
+ }
124
+
125
+ export function buildAgentShareProto(input: AgentShareInput): AgentShare {
126
+ const agentRef = (input.agentRef?.slug || input.agentRef?.org) ? create(ApiResourceReferenceSchema, { ...input.agentRef, kind: 40 }) : undefined;
127
+ const messages = input.messages ? buildAgentShareMessagesProto(input.messages) : undefined;
128
+ const environmentRefs = input.environmentRefs?.map(r => create(ApiResourceReferenceSchema, { ...r, kind: 53 }));
129
+ return Object.assign(create(AgentShareSchema), {
130
+ apiVersion: "agentic.stigmer.ai/v1",
131
+ kind: "AgentShare",
132
+ metadata: Object.assign(create(ApiResourceMetadataSchema), {
133
+ name: input.name,
134
+ org: input.org,
135
+ ...(input.slug && { slug: input.slug }),
136
+ ...(input.labels && { labels: input.labels }),
137
+ ...(input.visibility && { visibility: input.visibility }),
138
+ }),
139
+ spec: Object.assign(create(AgentShareSpecSchema), stripUndefined({
140
+ agentRef,
141
+ enabled: input.enabled,
142
+ audience: input.audience,
143
+ allowedOrigins: input.allowedOrigins,
144
+ messages,
145
+ environmentRefs,
146
+ })),
147
+ }) as AgentShare;
148
+ }
@@ -23,6 +23,7 @@ export const GRANTABLE_ROLES: ReadonlyMap<ApiResourceKind, readonly IamRole[]> =
23
23
  [ApiResourceKind.skill, [IamRole.owner, IamRole.viewer]],
24
24
  [ApiResourceKind.mcp_server, [IamRole.owner, IamRole.viewer]],
25
25
  [ApiResourceKind.agent_instance, [IamRole.owner, IamRole.viewer]],
26
+ [ApiResourceKind.agent_share, [IamRole.owner, IamRole.viewer]],
26
27
  [ApiResourceKind.workflow, [IamRole.owner, IamRole.viewer]],
27
28
  [ApiResourceKind.workflow_instance, [IamRole.owner, IamRole.viewer]],
28
29
  [ApiResourceKind.workflow_execution, [IamRole.owner, IamRole.viewer]],
package/src/gen/client.ts CHANGED
@@ -4,6 +4,7 @@ import type { Transport } from "@connectrpc/connect";
4
4
  import { AgentClient } from "./agent.js";
5
5
  import { AgentExecutionClient } from "./agentexecution.js";
6
6
  import { AgentInstanceClient } from "./agentinstance.js";
7
+ import { AgentShareClient } from "./agentshare.js";
7
8
  import { ApiKeyClient } from "./apikey.js";
8
9
  import { ArtifactClient } from "./artifact.js";
9
10
  import { EnvironmentClient } from "./environment.js";
@@ -28,6 +29,7 @@ export class GeneratedClient {
28
29
  readonly agent: AgentClient;
29
30
  readonly agentExecution: AgentExecutionClient;
30
31
  readonly agentInstance: AgentInstanceClient;
32
+ readonly agentShare: AgentShareClient;
31
33
  readonly apiKey: ApiKeyClient;
32
34
  readonly artifact: ArtifactClient;
33
35
  readonly environment: EnvironmentClient;
@@ -51,6 +53,7 @@ export class GeneratedClient {
51
53
  this.agent = new AgentClient(transport);
52
54
  this.agentExecution = new AgentExecutionClient(transport);
53
55
  this.agentInstance = new AgentInstanceClient(transport);
56
+ this.agentShare = new AgentShareClient(transport);
54
57
  this.apiKey = new ApiKeyClient(transport);
55
58
  this.artifact = new ArtifactClient(transport);
56
59
  this.environment = new EnvironmentClient(transport);
@@ -79,6 +82,8 @@ export { AgentExecutionClient } from "./agentexecution.js";
79
82
  export { type AgentExecutionInput, type ExecutionConfigInput, type ContextManagementConfigInput, type AttachmentInput } from "./agentexecution.js";
80
83
  export { AgentInstanceClient } from "./agentinstance.js";
81
84
  export { type AgentInstanceInput } from "./agentinstance.js";
85
+ export { AgentShareClient } from "./agentshare.js";
86
+ export { type AgentShareInput, type AgentShareMessagesInput } from "./agentshare.js";
82
87
  export { ApiKeyClient } from "./apikey.js";
83
88
  export { type ApiKeyInput } from "./apikey.js";
84
89
  export { ArtifactClient } from "./artifact.js";
@@ -12,7 +12,7 @@ import { EnvironmentQueryController } from "@stigmer/protos/ai/stigmer/agentic/e
12
12
  import { EnvironmentValueSchema, EnvironmentSpecSchema, type EnvironmentValue } from "@stigmer/protos/ai/stigmer/agentic/environment/v1/spec_pb";
13
13
  import { ApiResourceKind } from "@stigmer/protos/ai/stigmer/commons/apiresource/apiresourcekind/api_resource_kind_pb";
14
14
  import { ApiResourceVisibility } from "@stigmer/protos/ai/stigmer/commons/apiresource/enum_pb";
15
- import { ApiResourceIdSchema, ApiResourceReferenceSchema, ApiResourceDeleteInputSchema } from "@stigmer/protos/ai/stigmer/commons/apiresource/io_pb";
15
+ import { ApiResourceIdSchema, ApiResourceReferenceSchema, ApiResourceDeleteInputSchema, type UpdateVisibilityInput } from "@stigmer/protos/ai/stigmer/commons/apiresource/io_pb";
16
16
  import { ApiResourceMetadataSchema } from "@stigmer/protos/ai/stigmer/commons/apiresource/metadata_pb";
17
17
 
18
18
  /** Provides operations on environment resources. */
@@ -43,6 +43,12 @@ export class EnvironmentClient {
43
43
  } catch (e) { throw wrapError(e); }
44
44
  }
45
45
 
46
+ async updateVisibility(input: UpdateVisibilityInput): Promise<Environment> {
47
+ try {
48
+ return await this.command.updateVisibility(input);
49
+ } catch (e) { throw wrapError(e); }
50
+ }
51
+
46
52
  async delete(input: DeleteResourceInput): Promise<Environment> {
47
53
  try {
48
54
  return await this.command.delete(create(ApiResourceDeleteInputSchema, {
@@ -14,7 +14,7 @@ import { PlatformClientCommandController } from "@stigmer/protos/ai/stigmer/iam/
14
14
  import { PlatformClientCreateResponseSchema, PlatformClientIdSchema, ListPlatformClientsByOrgInputSchema, PlatformClientsSchema, type PlatformClientCreateResponse, type ListPlatformClientsByOrgInput, type PlatformClients } from "@stigmer/protos/ai/stigmer/iam/platformclient/v1/io_pb";
15
15
  import { PlatformClientQueryController } from "@stigmer/protos/ai/stigmer/iam/platformclient/v1/query_pb";
16
16
  import { PlatformClientSpecSchema } from "@stigmer/protos/ai/stigmer/iam/platformclient/v1/spec_pb";
17
- import { PlatformClientTokenController, MintUserTokenRequestSchema, MintUserTokenResponseSchema, type MintUserTokenRequest, type MintUserTokenResponse } from "@stigmer/protos/ai/stigmer/iam/platformclient/v1/token_pb";
17
+ import { PlatformClientTokenController, MintUserTokenRequestSchema, MintUserTokenResponseSchema, MintGuestTokenRequestSchema, MintGuestTokenResponseSchema, type MintUserTokenRequest, type MintUserTokenResponse, type MintGuestTokenRequest, type MintGuestTokenResponse } from "@stigmer/protos/ai/stigmer/iam/platformclient/v1/token_pb";
18
18
  import { IamRole } from "@stigmer/protos/ai/stigmer/iam/v1/enum_pb";
19
19
 
20
20
  /** Provides operations on platformclient resources. */
@@ -80,6 +80,12 @@ export class PlatformClientClient {
80
80
  return await this.token.mintUserToken(input);
81
81
  } catch (e) { throw wrapError(e); }
82
82
  }
83
+
84
+ async mintGuestToken(input: MintGuestTokenRequest): Promise<MintGuestTokenResponse> {
85
+ try {
86
+ return await this.token.mintGuestToken(input);
87
+ } catch (e) { throw wrapError(e); }
88
+ }
83
89
  }
84
90
 
85
91
  /** Input for creating/updating a PlatformClient. */
@@ -9,7 +9,7 @@ import { ToolApprovalOverrideSchema, McpServerUsageSchema } from "@stigmer/proto
9
9
  import { SessionSchema, type Session } from "@stigmer/protos/ai/stigmer/agentic/session/v1/api_pb";
10
10
  import { SessionCommandController } from "@stigmer/protos/ai/stigmer/agentic/session/v1/command_pb";
11
11
  import { Harness, CursorMode, ExecutionTarget, GitWriteBackMode } from "@stigmer/protos/ai/stigmer/agentic/session/v1/enum_pb";
12
- import { SessionIdSchema, UpdateSessionSubjectRequestSchema, ListSessionsRequestSchema, SessionListSchema, ListSessionsByAgentRequestSchema, type UpdateSessionSubjectRequest, type ListSessionsRequest, type SessionList, type ListSessionsByAgentRequest } from "@stigmer/protos/ai/stigmer/agentic/session/v1/io_pb";
12
+ import { SessionIdSchema, UpdateSessionSubjectRequestSchema, ListSessionsRequestSchema, SessionListSchema, ListSessionsByAgentInstanceRequestSchema, type UpdateSessionSubjectRequest, type ListSessionsRequest, type SessionList, type ListSessionsByAgentInstanceRequest } from "@stigmer/protos/ai/stigmer/agentic/session/v1/io_pb";
13
13
  import { SessionQueryController } from "@stigmer/protos/ai/stigmer/agentic/session/v1/query_pb";
14
14
  import { SessionSpecSchema } from "@stigmer/protos/ai/stigmer/agentic/session/v1/spec_pb";
15
15
  import { GitRepoSourceSchema, LocalPathSourceSchema, WorkspaceSourceSchema, WorkspaceEntrySchema } from "@stigmer/protos/ai/stigmer/agentic/session/v1/workspace_pb";
@@ -69,9 +69,9 @@ export class SessionClient {
69
69
  } catch (e) { throw wrapError(e); }
70
70
  }
71
71
 
72
- async listByAgent(input: ListSessionsByAgentRequest): Promise<SessionList> {
72
+ async listByAgentInstance(input: ListSessionsByAgentInstanceRequest): Promise<SessionList> {
73
73
  try {
74
- return await this.query.listByAgent(input);
74
+ return await this.query.listByAgentInstance(input);
75
75
  } catch (e) { throw wrapError(e); }
76
76
  }
77
77
  }
@@ -0,0 +1,334 @@
1
+ import { createGrpcWebTransport } from "@connectrpc/connect-web";
2
+ import { createClient, type Client } from "@connectrpc/connect";
3
+ import { create } from "@bufbuild/protobuf";
4
+ import { PlatformClientTokenController } from "@stigmer/protos/ai/stigmer/iam/platformclient/v1/token_pb";
5
+ import { MintGuestTokenRequestSchema } from "@stigmer/protos/ai/stigmer/iam/platformclient/v1/token_pb";
6
+ import { wrapError } from "./gen/errors.js";
7
+ import {
8
+ rpcMetadataInterceptor,
9
+ errorStripInterceptor,
10
+ } from "./internal/interceptors.js";
11
+
12
+ /**
13
+ * Minimal persistence contract for the visitor's guest id.
14
+ *
15
+ * Matches the subset of the Web Storage API that {@link GuestAuth}
16
+ * needs, so `localStorage` satisfies it directly. Inject a custom
17
+ * implementation for non-browser environments or tests.
18
+ */
19
+ export interface GuestIdStorage {
20
+ getItem(key: string): string | null;
21
+ setItem(key: string, value: string): void;
22
+ }
23
+
24
+ /**
25
+ * Configuration for a guest token-minting helper.
26
+ *
27
+ * Unlike {@link PlatformClientAuthConfig}, this carries **no
28
+ * credentials** — `mintGuestToken` is a public RPC gated server-side
29
+ * on an enabled AgentShare at the given org/slug. It is therefore safe
30
+ * to use directly from a browser.
31
+ *
32
+ * @example
33
+ * ```typescript
34
+ * import { createGuestAuth } from "@stigmer/sdk";
35
+ *
36
+ * const guestAuth = createGuestAuth({
37
+ * baseUrl: "https://api.stigmer.ai",
38
+ * org: "acme",
39
+ * slug: "support-agent",
40
+ * });
41
+ * ```
42
+ */
43
+ export interface GuestAuthConfig {
44
+ /** Stigmer API server URL (e.g., "https://api.stigmer.ai"). */
45
+ readonly baseUrl: string;
46
+
47
+ /** Organization slug from the share URL. */
48
+ readonly org: string;
49
+
50
+ /**
51
+ * Share slug from the share URL. Defaults to the shared agent's slug
52
+ * when the owner never customized it, so existing links pass the
53
+ * agent's slug here unchanged.
54
+ */
55
+ readonly slug: string;
56
+
57
+ /**
58
+ * Where to persist the visitor's guest id across visits.
59
+ *
60
+ * Defaults to `localStorage` when available, falling back to
61
+ * in-memory storage (guest identity then lasts one page load).
62
+ * The stored value is an opaque server-generated id — not a
63
+ * credential — that keys this browser's session read-isolation.
64
+ */
65
+ readonly storage?: GuestIdStorage;
66
+
67
+ /**
68
+ * Web origin of the page embedding the shared agent.
69
+ *
70
+ * Set this when the chat runs embedded — inside an iframe (the
71
+ * embedding page's origin, discovered via `@stigmer/embed`'s
72
+ * `resolveParentOrigin`) or directly in your own app
73
+ * (`window.location.origin`). Leave unset on the unframed hosted
74
+ * page. The server validates it against the share's
75
+ * `allowed_origins` at mint (an empty list admits any origin) and
76
+ * refuses with `"permission-denied"` when the origin is not allowed.
77
+ */
78
+ readonly embedOrigin?: string;
79
+
80
+ /**
81
+ * Share-link token from the URL's `?k=` parameter.
82
+ *
83
+ * Required when the share link has been locked with a rotatable
84
+ * token; harmless (ignored server-side) on plain links. On a locked
85
+ * link a missing or rotated-away token refuses the mint with
86
+ * `"not-found"` — deliberately indistinguishable from an agent that
87
+ * does not exist.
88
+ */
89
+ readonly linkToken?: string;
90
+ }
91
+
92
+ /**
93
+ * How long before expiry a cached guest token is considered stale.
94
+ * Re-minting inside this window keeps long-lived streams from starting
95
+ * with a token that expires moments later.
96
+ */
97
+ const EXPIRY_SKEW_MS = 60_000;
98
+
99
+ /** Storage key prefix for the persisted guest id, namespaced per org. */
100
+ const GUEST_ID_STORAGE_PREFIX = "stigmer:guest-id:";
101
+
102
+ /**
103
+ * Guest token-minting helper for shared-agent pages.
104
+ *
105
+ * A minimal, purpose-built companion to the main Stigmer client: it
106
+ * lazily mints short-lived guest JWTs via the public `mintGuestToken`
107
+ * RPC, caches them in memory until just before expiry, and persists
108
+ * the server-issued guest id so the same browser resolves to the same
109
+ * guest across visits.
110
+ *
111
+ * Wire {@link getAccessToken} into a `Stigmer` client — the SDK calls
112
+ * it per request, so refresh is automatic and no token ever needs to
113
+ * be stored outside memory.
114
+ *
115
+ * Failure semantics: when minting fails (e.g. sharing was revoked —
116
+ * the server answers NOT_FOUND, indistinguishable from "no such
117
+ * agent"), {@link getAccessToken} rejects with a `StigmerError`, which
118
+ * fails the triggering request with the real cause instead of sending
119
+ * it unauthenticated.
120
+ *
121
+ * @example
122
+ * ```typescript
123
+ * const guestAuth = createGuestAuth({
124
+ * baseUrl: "https://api.stigmer.ai",
125
+ * org: "acme",
126
+ * slug: "support-agent",
127
+ * });
128
+ *
129
+ * const client = new Stigmer({
130
+ * baseUrl: "https://api.stigmer.ai",
131
+ * getAccessToken: guestAuth.getAccessToken,
132
+ * });
133
+ * ```
134
+ */
135
+ export class GuestAuth {
136
+ private readonly tokenClient: Client<typeof PlatformClientTokenController>;
137
+ private readonly org: string;
138
+ private readonly slug: string;
139
+ private readonly storage: GuestIdStorage;
140
+ private readonly storageKey: string;
141
+ private readonly embedOrigin: string;
142
+ private readonly linkToken: string;
143
+
144
+ private cached: { accessToken: string; expiresAt: number } | null = null;
145
+ private pendingMint: Promise<string> | null = null;
146
+
147
+ /** @internal Use {@link createGuestAuth} instead. */
148
+ constructor(config: GuestAuthConfig) {
149
+ this.org = config.org;
150
+ this.slug = config.slug;
151
+ this.storage = config.storage ?? resolveDefaultStorage();
152
+ this.storageKey = `${GUEST_ID_STORAGE_PREFIX}${config.org}`;
153
+ this.embedOrigin = config.embedOrigin ?? "";
154
+ this.linkToken = config.linkToken ?? "";
155
+
156
+ const transport = createGrpcWebTransport({
157
+ baseUrl: config.baseUrl,
158
+ interceptors: [rpcMetadataInterceptor, errorStripInterceptor],
159
+ });
160
+
161
+ this.tokenClient = createClient(PlatformClientTokenController, transport);
162
+ }
163
+
164
+ /**
165
+ * The visitor's persisted guest id, or `null` before the first
166
+ * successful mint in this browser.
167
+ */
168
+ get guestCookieId(): string | null {
169
+ return safeGetItem(this.storage, this.storageKey);
170
+ }
171
+
172
+ /**
173
+ * Token provider for `StigmerConfig.getAccessToken`.
174
+ *
175
+ * Returns the cached guest JWT while it has more than a minute of
176
+ * life left; otherwise mints a fresh one. Concurrent callers during
177
+ * a mint share the same in-flight request (single-flight).
178
+ *
179
+ * Defined as an arrow property so it can be passed detached:
180
+ * `new Stigmer({ ..., getAccessToken: guestAuth.getAccessToken })`.
181
+ *
182
+ * @throws {StigmerError} with code `"not-found"` when the agent is
183
+ * not shared (or sharing was revoked — the server keeps the two
184
+ * indistinguishable by design)
185
+ * @throws {StigmerError} with code `"permission-denied"` when
186
+ * `embedOrigin` is not in the agent's `allowed_origins` — embeds
187
+ * should hide the widget on this code rather than surface an error
188
+ * @throws {StigmerError} with code `"invalid-argument"` when org or
189
+ * slug is malformed
190
+ */
191
+ readonly getAccessToken = async (): Promise<string | null> => {
192
+ if (this.cached && this.cached.expiresAt - Date.now() > EXPIRY_SKEW_MS) {
193
+ return this.cached.accessToken;
194
+ }
195
+
196
+ // Single-flight: the provider is invoked once per request, so a
197
+ // burst at page load (registry fetches + profile resolution) must
198
+ // collapse into one mint — both for latency and because concurrent
199
+ // first mints would otherwise race to persist different guest ids.
200
+ this.pendingMint ??= this.mint().finally(() => {
201
+ this.pendingMint = null;
202
+ });
203
+
204
+ return this.pendingMint;
205
+ };
206
+
207
+ private async mint(): Promise<string> {
208
+ try {
209
+ const response = await this.tokenClient.mintGuestToken(
210
+ create(MintGuestTokenRequestSchema, {
211
+ org: this.org,
212
+ slug: this.slug,
213
+ guestCookieId: safeGetItem(this.storage, this.storageKey) ?? "",
214
+ embedOrigin: this.embedOrigin,
215
+ linkToken: this.linkToken,
216
+ }),
217
+ );
218
+
219
+ safeSetItem(this.storage, this.storageKey, response.guestCookieId);
220
+ this.cached = {
221
+ accessToken: response.accessToken,
222
+ expiresAt: Date.now() + response.expiresIn * 1000,
223
+ };
224
+ return response.accessToken;
225
+ } catch (e) {
226
+ throw wrapError(e);
227
+ }
228
+ }
229
+ }
230
+
231
+ /**
232
+ * `localStorage` when usable, otherwise an in-memory fallback.
233
+ *
234
+ * Access is probed inside try/catch because merely touching
235
+ * `localStorage` can throw (e.g. sandboxed iframes with storage
236
+ * blocked). With the fallback, the chat still works — the visitor
237
+ * just gets a fresh guest identity per page load.
238
+ *
239
+ * `globalThis` is typed structurally because this package compiles
240
+ * without DOM libs (it is also consumed from Node, where
241
+ * `localStorage` does not exist).
242
+ */
243
+ function resolveDefaultStorage(): GuestIdStorage {
244
+ try {
245
+ const { localStorage: storage } = globalThis as {
246
+ localStorage?: GuestIdStorage & { removeItem(key: string): void };
247
+ };
248
+ if (storage) {
249
+ const probeKey = "stigmer:storage-probe";
250
+ storage.setItem(probeKey, "1");
251
+ storage.removeItem(probeKey);
252
+ return storage;
253
+ }
254
+ } catch {
255
+ // Fall through to the in-memory storage below.
256
+ }
257
+
258
+ const memory = new Map<string, string>();
259
+ return {
260
+ getItem: (key) => memory.get(key) ?? null,
261
+ setItem: (key, value) => {
262
+ memory.set(key, value);
263
+ },
264
+ };
265
+ }
266
+
267
+ /**
268
+ * Storage reads/writes never fail the mint: the guest id is a
269
+ * convenience (stable identity across visits), not a requirement —
270
+ * the server issues a fresh id when none is presented.
271
+ */
272
+ function safeGetItem(storage: GuestIdStorage, key: string): string | null {
273
+ try {
274
+ return storage.getItem(key);
275
+ } catch {
276
+ return null;
277
+ }
278
+ }
279
+
280
+ function safeSetItem(storage: GuestIdStorage, key: string, value: string): void {
281
+ try {
282
+ storage.setItem(key, value);
283
+ } catch {
284
+ // Persisting the id is best-effort; see safeGetItem.
285
+ }
286
+ }
287
+
288
+ /**
289
+ * Create a guest token-minting helper for a shared agent's hosted page
290
+ * or embed.
291
+ *
292
+ * This is the browser-side counterpart of `createPlatformClientAuth`:
293
+ * it involves **no credentials** — the server gates minting on an
294
+ * enabled AgentShare and issues a short-lived guest JWT scoped to the
295
+ * sharing org. Pass {@link GuestAuth.getAccessToken} to a `Stigmer`
296
+ * client and every request authenticates as this visitor.
297
+ *
298
+ * @example
299
+ * ```typescript
300
+ * import { Stigmer, createGuestAuth } from "@stigmer/sdk";
301
+ *
302
+ * const guestAuth = createGuestAuth({
303
+ * baseUrl: "https://api.stigmer.ai",
304
+ * org: "acme",
305
+ * slug: "support-agent",
306
+ * });
307
+ *
308
+ * const client = new Stigmer({
309
+ * baseUrl: "https://api.stigmer.ai",
310
+ * getAccessToken: guestAuth.getAccessToken,
311
+ * });
312
+ * ```
313
+ *
314
+ * @throws {Error} if `baseUrl`, `org`, or `slug` is missing or empty
315
+ */
316
+ export function createGuestAuth(config: GuestAuthConfig): GuestAuth {
317
+ if (!config.baseUrl) {
318
+ throw new Error(
319
+ "createGuestAuth: baseUrl is required (e.g., \"https://api.stigmer.ai\")",
320
+ );
321
+ }
322
+ if (!config.org) {
323
+ throw new Error(
324
+ "createGuestAuth: org is required — the organization slug from the share URL",
325
+ );
326
+ }
327
+ if (!config.slug) {
328
+ throw new Error(
329
+ "createGuestAuth: slug is required — the share slug from the share URL",
330
+ );
331
+ }
332
+
333
+ return new GuestAuth(config);
334
+ }
package/src/index.ts CHANGED
@@ -13,6 +13,26 @@ export {
13
13
  // Configuration
14
14
  export { type StigmerConfig, type TokenProvider } from "./config.js";
15
15
 
16
+ // Guest auth (shared-agent pages and embeds; browser-safe, credential-free)
17
+ export {
18
+ createGuestAuth,
19
+ GuestAuth,
20
+ type GuestAuthConfig,
21
+ type GuestIdStorage,
22
+ } from "./guest-auth.js";
23
+
24
+ // Agent sharing (hosted link + embed snippet; framework-free)
25
+ export {
26
+ MAX_ALLOWED_ORIGINS,
27
+ LINK_TOKEN_PARAM,
28
+ validateOrigin,
29
+ chatPath,
30
+ buildChatUrl,
31
+ appendLinkToken,
32
+ buildEmbedLoaderUrl,
33
+ buildEmbedSnippet,
34
+ } from "./sharing.js";
35
+
16
36
  // Error handling
17
37
  export {
18
38
  StigmerError,
@@ -125,6 +145,11 @@ export {
125
145
  AgentInstanceClient,
126
146
  type AgentInstanceInput,
127
147
  } from "./gen/agentinstance.js";
148
+ export {
149
+ AgentShareClient,
150
+ type AgentShareInput,
151
+ type AgentShareMessagesInput,
152
+ } from "./gen/agentshare.js";
128
153
  export { ApiKeyClient, type ApiKeyInput } from "./gen/apikey.js";
129
154
  export {
130
155
  EnvironmentClient,