@opengeni/runtime 3.0.1 → 4.0.0

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 (99) hide show
  1. package/dist/assets/codemode-client.json +1 -0
  2. package/dist/chunk-4A4XTKKX.js +1 -0
  3. package/dist/{chunk-RIJ6DCYS.js → chunk-5HJEVOMJ.js} +9817 -9494
  4. package/dist/chunk-5HJEVOMJ.js.map +1 -0
  5. package/dist/{chunk-FHGLYLBL.js → chunk-SPT3PLMX.js} +1 -87
  6. package/dist/chunk-SPT3PLMX.js.map +1 -0
  7. package/dist/{chunk-GB763TSV.js → chunk-TE27J65Q.js} +1 -1
  8. package/dist/chunk-TE27J65Q.js.map +1 -0
  9. package/dist/{chunk-46BFUGFL.js → chunk-WIHICNWS.js} +1660 -388
  10. package/dist/chunk-WIHICNWS.js.map +1 -0
  11. package/dist/{chunk-UU6EJR52.js → chunk-YU4VRTR4.js} +2 -2
  12. package/dist/chunk-YU4VRTR4.js.map +1 -0
  13. package/dist/chunk-ZG2YH25H.js +94 -0
  14. package/dist/chunk-ZG2YH25H.js.map +1 -0
  15. package/dist/codex-tool-search.d.ts +5 -4
  16. package/dist/context-compaction.d.ts +19 -0
  17. package/dist/gmail-rest-mcp.js +1 -1
  18. package/dist/index.d.ts +30 -22
  19. package/dist/index.js +25 -5
  20. package/dist/lazy-manifest-OEYEHNVY.js +45 -0
  21. package/dist/lazy-manifest-OEYEHNVY.js.map +1 -0
  22. package/dist/lazy-manifest.d.ts +6 -0
  23. package/dist/lazy-tool-transport.d.ts +3 -2
  24. package/dist/{localSources-PVFCAPN2.js → localSources-TWHTEKGO.js} +3 -2
  25. package/dist/localSources-TWHTEKGO.js.map +1 -0
  26. package/dist/metrics.d.ts +6 -0
  27. package/dist/operational-instructions.d.ts +1 -1
  28. package/dist/prepared-compaction-request.d.ts +8 -0
  29. package/dist/provider-history-adapter.d.ts +2 -2
  30. package/dist/runtime-skills.d.ts +3 -10
  31. package/dist/sandbox/codemode-client.d.ts +17 -0
  32. package/dist/sandbox/codemode-token.d.ts +3 -3
  33. package/dist/sandbox/index.d.ts +2 -1
  34. package/dist/sandbox/index.js +8 -2
  35. package/dist/sandbox/materialization-verification-error.d.ts +27 -0
  36. package/dist/sandbox/provider-command-session.d.ts +44 -2
  37. package/dist/sandbox/providers/modal-command-control.d.ts +32 -29
  38. package/dist/sandbox/providers/modal-command-router-wire.d.ts +49 -0
  39. package/dist/sandbox/providers/modal-command-session.d.ts +2 -1
  40. package/dist/sandbox/providers/modal-legacy-command-control.d.ts +50 -0
  41. package/dist/sandbox/providers/modal-materialization-verification.d.ts +7 -0
  42. package/dist/sandbox/providers/modal.d.ts +0 -8
  43. package/dist/sandbox/routing/routing-session.d.ts +21 -0
  44. package/dist/sandbox/turn-tool-cancellation.d.ts +1 -0
  45. package/dist/skill-library.d.ts +0 -6
  46. package/dist/skill-library.js +1 -1
  47. package/dist/workspace-tool-gateway.js +7 -5
  48. package/package.json +9 -7
  49. package/src/bundled_artifact_skills/opengeni-documents/SKILL.md +19 -0
  50. package/src/bundled_artifact_skills/opengeni-documents/references/api.md +22 -0
  51. package/src/bundled_default_skills/opengeni-client/SKILL.md +349 -0
  52. package/src/bundled_default_skills/opengeni-client/agents/openai.yaml +4 -0
  53. package/src/bundled_default_skills/opengeni-client/references/api-workflows.md +256 -0
  54. package/src/bundled_default_skills/opengeni-client/references/compatibility-and-troubleshooting.md +84 -0
  55. package/src/bundled_default_skills/opengeni-client/references/customer-skill-template.md +96 -0
  56. package/src/bundled_default_skills/opengeni-client/references/data-tools-and-credentials.md +99 -0
  57. package/src/bundled_default_skills/opengeni-client/references/discovery-and-autonomy.md +81 -0
  58. package/src/bundled_default_skills/opengeni-client/references/external-users-and-connect.md +457 -0
  59. package/src/bundled_default_skills/opengeni-client/references/isolation-and-authorization.md +77 -0
  60. package/src/bundled_default_skills/opengeni-client/references/product-integration-shapes.md +232 -0
  61. package/src/bundled_default_skills/opengeni-client/references/product-shapes-and-ui.md +116 -0
  62. package/src/bundled_default_skills/opengeni-client/references/runtime-profile-and-verification.md +157 -0
  63. package/src/bundled_default_skills/opengeni-help/SKILL.md +101 -0
  64. package/src/bundled_management_skills/opengeni-skills/SKILL.md +33 -3
  65. package/src/codex-tool-search.ts +13 -5
  66. package/src/context-compaction.ts +87 -4
  67. package/src/gmail-rest-mcp.ts +1 -2
  68. package/src/index.ts +363 -131
  69. package/src/lazy-manifest.ts +55 -0
  70. package/src/lazy-tool-transport.ts +15 -2
  71. package/src/metrics.ts +6 -0
  72. package/src/model-request-capture.ts +3 -0
  73. package/src/operational-instructions.ts +11 -2
  74. package/src/prepared-compaction-request.ts +51 -0
  75. package/src/provider-history-adapter.ts +12 -3
  76. package/src/runtime-skills.ts +3 -17
  77. package/src/sandbox/codemode-client.ts +204 -0
  78. package/src/sandbox/codemode-token.ts +7 -2
  79. package/src/sandbox/index.ts +7 -0
  80. package/src/sandbox/materialization-verification-error.ts +65 -0
  81. package/src/sandbox/provider-command-session.ts +70 -2
  82. package/src/sandbox/providers/modal-command-control.ts +386 -242
  83. package/src/sandbox/providers/modal-command-router-wire.ts +248 -0
  84. package/src/sandbox/providers/modal-command-session.ts +177 -4
  85. package/src/sandbox/providers/modal-legacy-command-control.ts +351 -0
  86. package/src/sandbox/providers/modal-materialization-verification.ts +122 -0
  87. package/src/sandbox/providers/modal.ts +1 -9
  88. package/src/sandbox/routing/routing-session.ts +417 -79
  89. package/src/sandbox/turn-tool-cancellation.ts +38 -2
  90. package/src/skill-catalog.ts +1 -0
  91. package/src/skill-library.ts +0 -6
  92. package/src/workspace-governance.ts +0 -1
  93. package/src/workspace-skills.ts +114 -74
  94. package/dist/chunk-46BFUGFL.js.map +0 -1
  95. package/dist/chunk-FHGLYLBL.js.map +0 -1
  96. package/dist/chunk-GB763TSV.js.map +0 -1
  97. package/dist/chunk-RIJ6DCYS.js.map +0 -1
  98. package/dist/chunk-UU6EJR52.js.map +0 -1
  99. /package/dist/{localSources-PVFCAPN2.js.map → chunk-4A4XTKKX.js.map} +0 -0
@@ -0,0 +1,457 @@
1
+ # External users and embedded connection setup
2
+
3
+ Use these APIs only when the installed SDK and deployment expose them. This
4
+ guide describes implemented external identity, service lifecycle, core
5
+ Personal/private session access, and curated OAuth paths—not completion of every
6
+ white-label surface. Optional native linking is explicit delegation, not account
7
+ merging. Full provider coverage remains unfinished. Do not infer guarantees from
8
+ the presence of a contract type.
9
+
10
+ ## Optional use of an existing native account
11
+
12
+ Ordinary embedding needs only `asUser`; never require native registration or
13
+ linking for a product user. For an existing OpenGeni user who deliberately wants
14
+ the product to use their native workspace access, begin a link through the
15
+ external client with `beginIdentityLink`. Show the returned challenge only in
16
+ the native consent URL fragment, never its query, logs or analytics. The native
17
+ `/identity-links/:linkId?organization=:organizationId#challenge=:challenge` page
18
+ requires the user's real native login, displays both identities and the requested
19
+ permissions, and permits narrowing before confirmation. The fragment is scrubbed
20
+ before the application mounts. A page reload requires reopening the original
21
+ consent URL. Poll `getIdentityLink` from the product backend for confirmation.
22
+
23
+ After explicit confirmation, choose linked mode on the backend:
24
+
25
+ ```ts
26
+ const linked = serviceClient.asLinkedUser(authenticatedUser.id, {
27
+ source: "my-product",
28
+ linkId: confirmedLink.id,
29
+ expectedLinkRevision: confirmedLink.revision,
30
+ });
31
+ ```
32
+
33
+ Use the same source and opaque ID used at initiation. A confirmed link does not
34
+ change `asUser`, move external sessions or credentials, or merge accounts. New
35
+ linked work belongs to the native user. Requests intersect the key's permissions,
36
+ the live native user's permissions and the approved link ceiling. Never fall back
37
+ to service or external mode when linked authorization fails.
38
+
39
+ Link expiry is optional; null means until revoked. Either confirmed participant
40
+ can revoke using the observed revision. Accepted linked turns, scheduled task
41
+ revisions, child sessions and causal continuations retain the link restriction;
42
+ revocation denies later execution without requiring the original API key to stay
43
+ active. This is an execution-time check, not a promise to undo a remote operation
44
+ already started. Native access to native-owned resources remains intact. Do not
45
+ assume an external user's personal connection transfers to the native owner
46
+ through a link: provision or connect separately while explicitly acting as that
47
+ user. Linked work independently retains the live link restriction.
48
+ `listIdentityLinks(workspaceId, cursor?)`
49
+ provides a participant-only inventory, and native workspace settings expose the
50
+ same list/revoke behavior without retaining the original consent URL.
51
+
52
+ ## Separate service administration from user requests
53
+
54
+ Keep one organization-key client on the trusted product backend. After product
55
+ authentication, derive the immutable external identity from the server session:
56
+
57
+ ```ts
58
+ const actor = serviceClient.asUser(authenticatedUser.id, { source: "my-product" });
59
+ const transport = actor.connectTransport();
60
+ const providers = await transport.catalog(authorizedWorkspaceId);
61
+ ```
62
+
63
+ Here `serviceClient`, `authenticatedUser`, and `authorizedWorkspaceId` are
64
+ host-owned dependencies, not fields accepted from the browser. `asUser` creates
65
+ a separate client and does not mutate the service client. It requires an
66
+ organization key; a workspace key or deployment access key is not a substitute.
67
+ Never retry a denied user request using the unscoped service client.
68
+
69
+ An organization key is trusted to assert and lazily provision product users;
70
+ there is no separate provisioning permission or registration ceremony. The first
71
+ authenticated request may create the identity anchor even if its later workspace
72
+ operation is denied. Workload permissions still restrict that operation. Derive
73
+ IDs from authenticated host records and bound onboarding in the host; never
74
+ forward arbitrary browser-supplied identities. Personal identity anchors are not
75
+ shared product-tenant workspaces or a way to obtain workspace membership.
76
+
77
+ Identity is scoped by organization, source and opaque external ID. Source
78
+ defaults to `default`; use a stable source namespace when multiple identity
79
+ systems share an organization. IDs are case-sensitive, not emails to normalize:
80
+ maximum 1024 UTF-8 bytes for the ID and 200 for source; empty strings, NUL and
81
+ invalid Unicode are rejected. A native-looking ID does not impersonate a native
82
+ user. Workspace mapping identity passed to `ensureWorkspace` is a separate
83
+ concept from this acting-user identity.
84
+
85
+ User mode lazily establishes an external identity but does not grant access to a
86
+ shared workspace. An explicitly authorized service onboarding operation may use:
87
+
88
+ ```ts
89
+ await serviceClient.addExternalWorkspaceMember(authorizedWorkspaceId, {
90
+ identity: { externalId: authenticatedUser.id, source: "my-product" },
91
+ permissions: ["workspace:read", "connections:read", "connections:write"],
92
+ operationId: onboardingOperationId,
93
+ });
94
+ ```
95
+
96
+ Do this only after the host has approved membership, not on every arbitrary
97
+ browser request. The service needs `members:manage` and may not grant authority
98
+ beyond its ceiling. Persist `onboardingOperationId` before the call. Exact keyed
99
+ replays return historical identity without restoring removed membership; different
100
+ permissions conflict instead of overwriting a subsequently reduced grant. Installation also
101
+ needs `capabilities:manage`; do not add it unless installation is a product
102
+ feature the user may perform. User requests intersect actual membership with
103
+ the initiating key's permissions. Service administration remains separate.
104
+
105
+ ### Removal and account-wide lifecycle
106
+
107
+ For recoverable onboarding, establish and retain the identity anchor before
108
+ granting membership. Service `lookupExternalIdentity(organizationId, identity)`
109
+ is non-provisioning and returns content-free identity/membership IDs, statuses and
110
+ separate revisions, including suspended/offboarded identities. It requires
111
+ `members:manage` and does not reactivate an identity. A missing result is not proof
112
+ that an earlier unkeyed request cannot still provision one.
113
+
114
+ To withdraw this external member's workspace access while fencing a pending keyed
115
+ grant, call `cancelExternalWorkspaceMemberGrant(organizationId, workspaceId,
116
+ organizationMembershipId, { operationId: cancellationOperationId,
117
+ cancelGrantOperationId: onboardingOperationId })`. Persist both distinct UUIDs;
118
+ retry the exact cancellation body after response loss. This reuses native
119
+ teardown and fences the named grant even if membership is absent. It withdraws
120
+ current workspace membership, not just one session. New grant IDs are explicit
121
+ new onboarding, never retries. Legacy requests without `operationId` remain
122
+ unfenced: drain old writers before claiming late-grant protection.
123
+
124
+ Use the service client's existing `removeWorkspaceMember(workspaceId, subjectId)`
125
+ to remove an external actor from a shared workspace. It requires `members:manage`,
126
+ rechecks the live key, preserves the last-admin guard, and uses the same fenced
127
+ settlement/cancellation path as native removal. It does not disable the actor in
128
+ other workspaces. Ordinary `asUser` reads never restore removed membership.
129
+
130
+ For account-wide changes, call `serviceClient.updateExternalIdentityMembership(
131
+ organizationId, organizationMembershipId, request)`. The membership ID comes from
132
+ the identity returned by onboarding; the initial membership authorization
133
+ revision is 1. The request contains `kind` (`suspend`, `reactivate`, or `offboard`),
134
+ `expectedAuthorizationRevision`, a UUID `operationId`, and optional `reason`.
135
+ Keep the returned membership revision for the next transition. Reuse the exact
136
+ operation ID and body when reconciling an uncertain response, never a different
137
+ transition under the old ID. Replay still requires live service authority.
138
+
139
+ This endpoint requires the organization service key's explicit `account:admin`;
140
+ the external-user lane and native-user targets are rejected. Suspension disables
141
+ new external admission and uses the canonical organization protocol to revoke
142
+ work and grants. Reactivation restores admission only: workspace memberships,
143
+ scheduled work, and resource grants are not restored. Offboarding is terminal
144
+ through this API and follows the existing organization retention policy; it is
145
+ not immediate deletion of history or upstream provider consent. Audit records
146
+ identify the service key separately from native administering memberships.
147
+
148
+ ### Personal workspaces and private sessions
149
+
150
+ An admitted external actor can access its exact provisioned Personal workspace;
151
+ `asUser` workspace discovery includes that pointer when the key permits
152
+ `workspace:read`. This is not a service-key fallback, and `ensureWorkspace`
153
+ continues to provision shared product-tenant workspaces only. Personal permissions
154
+ use the same non-administrative owner set as native Personal workspaces,
155
+ intersected with the key ceiling. No Personal member-management wildcard is added.
156
+
157
+ Core session creation, private-read authorization, listing, pinning, visibility
158
+ changes, and same-workspace fork operations use dedicated external owning-user
159
+ proof. The native-cookie flag remains false. Private creation still requires
160
+ platform readiness and, in shared workspaces, the existing organization private
161
+ session setting. Request-time session creation/tenancy commits recheck the live
162
+ key and identity generation; a failed recheck rolls back the mutation. Forking
163
+ private content into workspace visibility retains the existing explicit sharing
164
+ acknowledgment. Private sessions do not make shared-workspace Files or Sites
165
+ private. Full personal-resource, worker, stream, and scheduled-execution parity
166
+ still needs its own integrated verification; do not promise it from these core
167
+ session checks alone.
168
+
169
+ ## Host bridge and browser ownership
170
+
171
+ Expose only the Connect operations the product needs through authenticated,
172
+ same-origin backend routes. Every route must authenticate the host session,
173
+ derive the actor and workspace mapping server-side, and apply the host's normal
174
+ CSRF protection to mutations. Never forward an arbitrary upstream URL, actor
175
+ header, organization ID or bearer supplied by the browser. Validate request
176
+ bodies and return credential-free projections; redact errors before display or
177
+ logging. Forward cancellation without assuming it rolls back server effects.
178
+
179
+ The backend transport implements catalog, accounts, pending, begin, get,
180
+ advance, cancel and disconnect. A browser `ConnectTransport` calls those host
181
+ routes; it never contains the organization-key SDK client. Use one
182
+ `ConnectController` per authenticated actor/workspace and dispose it when either
183
+ changes. `@opengeni/react/connect` provides optional unstyled `ConnectChooser`,
184
+ `ConnectSetup`, `ConnectAccounts` and `useConnect`. `ConnectPanel` composes the
185
+ three surfaces; import `@opengeni/react/connect.css` for its opt-in scoped styles.
186
+ The host still owns navigation and controller lifetime. Controller replacement
187
+ clears pending credential forms and prior account/catalog views.
188
+ Providing `returnUrl` to `ConnectAccounts` enables explicit reconnect bound to
189
+ the selected account's provider, ownership and ID; `ConnectPanel` wires this
190
+ automatically. Reconnect does not silently substitute a different account.
191
+
192
+ For a host-owned capability library, `CapabilityCatalogRow` from the same
193
+ subpath supplies the shared icon/name/description row. Provide explicit
194
+ `status` (`available`, `added`, `attention`, `unavailable`, or `loading`) and
195
+ `onOpen`; the plus/check is decorative, not a second action. Normal status
196
+ labels remain accessible and exceptions remain visible. Use one setup entry
197
+ point from the catalog and conversation. Keep provider authentication,
198
+ workspace/account sharing, and explicit Plugin/Skill installation separate;
199
+ matching visual components never grants authority or implies installation.
200
+ When using `ConnectionCatalog`, provide each option's typed `state` to use the
201
+ quiet glyph treatment. Omitting it preserves the existing visible `status`
202
+ string, so older integrations cannot silently lose provider warnings.
203
+
204
+ Wire session timeline `onReconnect` to the host's connection experience. Use
205
+ `findConnectRecoveryAccount` from `@opengeni/connect` with fresh account metadata
206
+ and the event's exact connection ID, then begin setup for that account. A missing
207
+ ID or deleted account requires an explicit user choice, not a provider-name match.
208
+ The product can host the ordinary Connect experience in its own UI. The native
209
+ session and runnable host example use this same exact-account lookup.
210
+
211
+ ## Durable OAuth and explicit installation
212
+
213
+ 1. Read catalog readiness for the actual actor. The catalog covers generic,
214
+ curated and first-party Connect adapters. Model-account pools retain their
215
+ dedicated SDK APIs and device flow, described below; operator configuration
216
+ is not a user-connect action.
217
+ 2. Begin with explicit provider, ownership, a stable idempotency key and the
218
+ exact return URL chosen by the trusted host backend. Persist the attempt ID
219
+ in authenticated host state before navigation. Do not derive the return URL
220
+ from an unchecked browser field.
221
+ 3. For popup mode, invoke `authorizeConnectAttempt` directly from a user gesture
222
+ with `createBrowserConnectNavigation(window)`. Blocked popups are errors;
223
+ full redirect is an explicit host choice, not an automatic fallback.
224
+ 4. Recover through `get` or authenticated `pending`. The callback preserves the
225
+ stored return URL, including escaping and fragment, without adding status
226
+ parameters. Popup messages and URL parameters never prove completion.
227
+ 5. OAuth may commit credentials while the attempt remains
228
+ `connected_but_incomplete`. Advance with `retry` to review an integration
229
+ preview, then submit its preview ID/content hash and explicitly selected
230
+ operation IDs. Never assume OAuth installed all operations.
231
+ 6. Changed source requires a new preview and approval. Preserve revision and
232
+ idempotency fields on retries. An uncertain provider effect is not permission
233
+ to start a duplicate mutation with a fresh key.
234
+
235
+ Aborting polling stops observation, not setup. Explicit `cancel` stops setup
236
+ without revoking credentials already committed. `disconnect` currently revokes
237
+ local OpenGeni connection access, not upstream provider consent. Pass the observed
238
+ account `version` as `expectedVersion` to reject a stale selection. The shared
239
+ account component requires that version and explicit confirmation; an unknown
240
+ outcome requires live reload, not automatic replay. Provider-specific account
241
+ management remains unfinished.
242
+
243
+ ## Embedded Sites
244
+
245
+ `@opengeni/react/sites` exports `SiteList`, `SiteDetail` and the structural
246
+ `SiteClient` host-proxy interface. The SDK's existing published-artifact methods
247
+ implement it. `asUser` retains the public client class and artifact methods.
248
+ Sites remain workspace-shared artifacts, not private session outputs.
249
+
250
+ Use `SiteList.onOpen` for host navigation. `SiteDetail` reuses the existing
251
+ opaque-origin `PublishedHtmlArtifactFrame`; never introduce a second renderer
252
+ or put backend keys in HTML. Supply only an authenticated, filtered `toolBridge`.
253
+ Use `createSiteToolBridge` from `@opengeni/sdk/site` with the exact artifact ID,
254
+ version ID and that version's `requestedTools`. Provide an authenticated catalog
255
+ transport and `callTool` backed by the host's `callWorkspaceSiteTool` SDK method.
256
+ The native console uses this same bridge. Recreate it when the actor or version
257
+ changes. It strips iframe-supplied authority, pins the Site context, and retries
258
+ only an explicit pre-execution stale-catalog response, never an uncertain effect.
259
+ For the Site's ordinary session SDK, optionally supply `fetchResponse` with your
260
+ authenticated host transport. The shared bridge applies the same bounded
261
+ `siteSessionPath` routing as the native console and forwards only content negotiation
262
+ and event replay headers; host authentication and tenant selection remain outside
263
+ the iframe. The bridge adds its pinned Site ID/version headers (never trusts
264
+ iframe-supplied ones), enabling the API's verified Site-origin attribution on
265
+ new conversations. `originSiteId=current` resolves to that pinned Site for
266
+ conversation filtering. Provenance does not grant access or replace the acting
267
+ user/workspace authority. Keep these headers through your authenticated proxy;
268
+ do not synthesize provenance from caller-supplied session metadata.
269
+ Omit this transport for tools-only Sites. Display uses
270
+ `getWorkspaceArtifactHtml` at the observed version, not a retained-source download.
271
+ It checks Site read authority every 15 seconds while loaded and clears the frame
272
+ on denial, scope replacement or version/status change. This is bounded UI
273
+ revalidation, not instantaneous revocation of downloaded HTML; bridge calls must
274
+ independently enforce current backend authority.
275
+
276
+ `canPublish` controls presentation only. The backend still requires
277
+ `artifacts:publish`; rollback/archive/restore preserve the observed current
278
+ version and require explicit confirmation. Failed mutations clear the loaded
279
+ state and require refresh rather than an unsafe retry. Authoring buttons and
280
+ prompts belong to the host: create an ordinary authorized session and navigate
281
+ to your existing session UI. There is no dedicated SDK authoring helper or
282
+ branded Site component button. Native Site UI reuse
283
+ and complete visual acceptance remain separate integration work.
284
+
285
+ ## Credentials and focused acceptance
286
+
287
+ For a named curated API integration account, pass `installationTarget: {
288
+ instanceKey, displayName, expectedInstanceVersion? }` when beginning Connect.
289
+ Reconnect uses the exact current instance version; new accounts omit that version.
290
+ The attempt retains this choice through OAuth, operation preview and installation.
291
+ Without an explicit target, setup creates an independent account rather than
292
+ overwriting a default instance. OAuth success alone still requires operation review.
293
+
294
+ Fiken's `fiken-token` catalog entry is workspace-only. Submit the `apiToken` and
295
+ optional `defaultCompanySlug` fields through the credential action; OpenGeni verifies
296
+ the token and accessible companies before storage. Resume/replay the same attempt
297
+ and operation identity rather than submitting the secret to a new attempt after an
298
+ uncertain response. The separate workspace-only `fiken-oauth` entry uses the
299
+ deployment's registered Fiken OAuth application. It preserves the exact host
300
+ return URL and atomically commits the verified company account and completion
301
+ receipt. Reconnect checks the observed account version; callback replay does not
302
+ repeat the provider exchange. Neither adapter grants personal ownership.
303
+
304
+ Native/local/configured workspace setup and organization/workspace API-key setup
305
+ use the same durable Connect flow where the adapter supports them. Service keys
306
+ cannot create personal connections. Keep keys on the product backend; a callback
307
+ uses its signed initiating principal and current authority, not a new browser login.
308
+ An external Connect attempt also retains its original key/link restriction.
309
+ Changing clients does not replace it: if the initiating key or link was revoked,
310
+ start a new authorized setup rather than expecting a new key to revive the old
311
+ attempt. This short-lived setup rule is separate from accepted agent/scheduled
312
+ work, which does not depend on the original API key remaining active.
313
+
314
+ Use ordinary native connections for both backend provisioning and interactive
315
+ OAuth. An organization-admin backend uses `asUser` to provision a personal
316
+ connection for its canonical user; no separate signup or host binding is needed.
317
+ Persist provisioning operation IDs before requests and reuse them on uncertain
318
+ retries. Select the resulting connection through the native connection authority
319
+ fields; connection visibility is not permission for another participant to use
320
+ its credentials. OAuth refresh remains in the native connection engine.
321
+
322
+ The former host resolver, binding and delegation APIs and selected-host fields
323
+ are removed. Do not build against them or reinterpret their IDs as native
324
+ connection IDs. An optional external credential supplier is future work behind
325
+ the same connection model, not a second setup requirement. See
326
+ `docs/remote-mcp-credentials.md` for the cutover boundary.
327
+
328
+ The request-time workspace tool gateway accepts verified external users and
329
+ organization service keys. Tool catalog/operation permission filtering and
330
+ existing approval semantics still apply. The new lanes recheck current key and
331
+ identity/membership permission ceilings around provider preparation and invocation;
332
+ they do not authorize an agent attempt as a service or inherit a creator's rights.
333
+ The gateway uses the native resolver and rechecks the caller before physical
334
+ requests. It does not call an embedding-product credential callback.
335
+
336
+ Accepted native turn/task selection retains the named actor and exact connection
337
+ authority. A shared conversation does not borrow its creator's credentials for
338
+ another participant. Scheduled occurrences and supported child work consume the
339
+ captured native selection and recheck live authority; token refresh does not
340
+ change the selected account. Existing-session schedules use the target session's
341
+ tools and persisted MCP definitions. Do not add per-endpoint host delegation.
342
+
343
+ Realtime empty-shell creation captures no connection authority. Send its first
344
+ text under the authenticated participant with the native connection selection
345
+ and `clientEventId`; this grants no voice-provider authority.
346
+
347
+ Legacy OAuth starts without verified external continuations fail closed. Curated
348
+ OAuth uses the shared Connect panel. Generic MCP OAuth is also available through
349
+ `actor.startConnectionOAuth(workspaceId, { mcpUrl, returnUrl, ... })`: the trusted
350
+ host backend supplies an exact absolute HTTP(S) return URL, without credentials
351
+ or control/space characters. The signed state encrypts the external continuation;
352
+ callbacks recheck the live key/identity/workspace before exchange and credential
353
+ commit, and consume the nonce before exchange. Both success and failure return
354
+ to the original string without appended parameters; the host must reload
355
+ authenticated connection state rather than treating navigation as proof of
356
+ success. Native `returnPath` behavior is unchanged. The shared panel also offers
357
+ `mcp-oauth`: server URL input, OAuth navigation, pending recovery, account listing
358
+ and reconnect. Its callback commits credentials and the completion receipt in
359
+ one transaction; callback replay never exchanges the code again. Completion is
360
+ connection-only, not an installed integration or a grant to every server tool.
361
+ The `mcp-bearer` adapter accepts a server URL and bearer credential. The
362
+ `mcp-headers` adapter accepts the URL and a JSON object in the secret `headers`
363
+ field for single- or multi-header authentication; transport headers, duplicate
364
+ case-insensitive names and malformed values are rejected before any commit.
365
+ Both persist only
366
+ encrypted material, and uses keyed operation digests. HTTPS without URL userinfo
367
+ or fragment is required. Reconnect keeps the same destination and observed account
368
+ version. Its connection-only completion means the credential was saved, not that
369
+ the server validated it or tools were installed; the normal credential resolver
370
+ enforces the saved MCP destination when it is used. Dedicated workspace model
371
+ provider credentials still use their own guarded flows. Uncertain mutations are
372
+ not automatically retried with a new operation ID.
373
+ Explicit provider denial or missing authorization code before exchange terminates
374
+ the attempt with a replayable failure receipt; start a new attempt to authorize
375
+ again. Unknown exchange or persistence outcomes are not treated as safe retries.
376
+ Provider-specific completion is intentional: a saved credential does not mean
377
+ that repository access, a review webhook, or source synchronization is enabled.
378
+
379
+ `github-personal` preserves personal OAuth account and repository-selection proof.
380
+ `github-app` discovers installations, asks the host user to choose one, then
381
+ requires fresh owner proof before binding repository access. `github-lens` uses
382
+ the same chooser behavior but creates separate Review Bot registrations, webhook
383
+ routing and repository review bindings; it requires managed compute and
384
+ workspace administration plus secret-write permission.
385
+ Neither GitHub App flow is a generic stored user token. Pending organization-owner
386
+ approval is incomplete setup, not a connected account. Discovery currently supports
387
+ at most 99 existing installations plus the new-install option; a larger result
388
+ fails explicitly instead of silently selecting or dropping installations.
389
+
390
+ `slack-bot` is a workspace bot installation, while `slack-personal` is the official
391
+ personal Slack MCP authorization flow. Do not substitute one for the other.
392
+ `x` and `reddit` use the existing social-account domain and OAuth scopes. Workspace
393
+ social setup requires workspace administration; personal setup requires a verified
394
+ owning user. Native and host clients share callback receipts and exact returns.
395
+ Social accounts remain limited by the existing one-personal-account-per-provider
396
+ semantics. Account IDs with `social:`, `github-installation:` and `lens-registration:`
397
+ prefixes are opaque SDK identifiers; use Connect transport disconnect rather than
398
+ passing these to generic credential APIs.
399
+
400
+ Social reconnect requires the observed account ID and version. The callback must
401
+ prove the same upstream account and cannot overwrite a concurrent refresh,
402
+ disconnect or reconnect. Reload accounts after a conflict before asking the user
403
+ to start another attempt.
404
+
405
+ `mcp-install` installs an available, no-credential MCP capability after probing it;
406
+ it does not manufacture a connection. The credential-input action can contain
407
+ bounded `options` for fields: render these as selectors, not free-text account IDs.
408
+ The shared React setup surface already does this for MCP and API-source choices.
409
+
410
+ Model accounts retain their dedicated SDK and pool APIs rather than pretending
411
+ to be ordinary Connect credentials. `pollDeviceAuthorization` from
412
+ `@opengeni/connect` supplies bounded, abortable device polling, and
413
+ `DeviceAuthorization` from `@opengeni/react/connect` supplies optional presentation.
414
+ Keep opaque device state on the server. SuperGrok user pools require ordinary
415
+ workspace membership; a synthetic personal-workspace owner grant alone is not
416
+ enough. Verified external users follow the same restriction as native users.
417
+
418
+ Known Connect callbacks recover the exact saved return URL even after state
419
+ expiry. This is navigation recovery only: expired state cannot exchange or save
420
+ credentials. Poll the attempt for its actual status after returning.
421
+
422
+ `openapi` and `graphql` setup accepts a document/endpoint URL and an optional
423
+ existing connection ID. The server performs pinned source discovery and returns
424
+ an explicit operation preview. Public services do not create fake credentials.
425
+ Installation re-resolves the source and checks its revision/hash, ownership and
426
+ the selected operations. Personal installations require a personal connection.
427
+ The source is immutable once previewed. Each setup gets an independent named
428
+ installation unless the host deliberately supplies an observed instance target.
429
+
430
+ `atlassian`, `google-drive-knowledge` and `google-drive-publish` preserve the
431
+ first-party connectors rather than substituting curated API definitions.
432
+ They require personal ownership; completion means the credential was committed,
433
+ not that all projects, spaces or folders were selected or synchronized.
434
+ Atlassian source selection uses `browseAtlassianSources`, `saveAtlassianSources`
435
+ and `setAtlassianLifecycle`, retaining explicit destination, cadence and read
436
+ policy. Google Drive publishing requires an existing knowledge connection in
437
+ `reconnectAccountId`, additional provider consent and an explicitly picked writable
438
+ folder. Publication writes retain the existing default `ask` policy. Native
439
+ Atlassian and Drive connect buttons consume the same durable setup surface.
440
+
441
+ `examples/embedded-product` is a runnable loopback host reference with explicit
442
+ authentication/CSRF seams, shared Connect/Site UI, and Edit-with-Geni into ordinary
443
+ session hooks/timeline/approval/structured-input, versioned session control, and
444
+ shared durable composer/queue controls, explicit schedule management, and bounded
445
+ workspace-file uploads. Schedule operations retain native permission and approval
446
+ semantics, not a new execution delegation guarantee. Its fixed-user demo auth
447
+ is opt-in and must never be exposed publicly. Native and embedded authoring
448
+ prompts live in their respective products, not the SDK. The example's optional
449
+ trusted `completionHref` keeps completion links in the host product.
450
+ No helper grants tool permissions or changes model billing/approval/scheduling.
451
+
452
+ Test concurrent users without actor-header bleed, cross-workspace denial,
453
+ membership/key permission reduction, exact return URL preservation, pending
454
+ recovery after opener loss, duplicate callbacks, changed-source reapproval and
455
+ explicit operation selection. Keep existing session approvals and scheduling
456
+ semantics. Do not claim provider, browser or scheduled-renewal conformance from
457
+ a transport unit test.
@@ -0,0 +1,77 @@
1
+ # Isolation and authorization
2
+
3
+ ## Start from who may share, not from workspace count
4
+
5
+ An OpenGeni organization is the administrative and billing container. An organization workspace is the operational boundary for sessions, events, files, documents, connections, installed capabilities, workspace Knowledge, settings, and agent access.
6
+
7
+ Use the smallest group allowed to share those workspace-scoped capabilities as the workspace mapping unit:
8
+
9
+ | Product requirement | Default mapping | Why |
10
+ | --- | --- | --- |
11
+ | A team or tenant may collaborate across all chats | One workspace per team or tenant | Shared sessions and workspace resources match the product rule |
12
+ | Users share workspace resources but their conversations are private | One workspace per tenant; `asUser()` and private session visibility | Canonical ownership protects transcripts without duplicating shared resources |
13
+ | An agent must not reach even its user's other conversations | `agentAccess: "session"` | An additional task-tree boundary, independent of human visibility |
14
+ | Chats may share but data access differs by tenant | At least one workspace per data tenant | Provider authority must never span a tenant that may not share data |
15
+ | Different users access the same data but their chats are private | Shared workspace data and private sessions | Shared upstream data does not make a private transcript shared |
16
+
17
+ Other mappings are valid when the product explicitly accepts their sharing semantics. Document that decision; do not use workspace count alone as an optimization goal.
18
+
19
+ A workspace is control-plane state, not a dedicated cluster or permanently running sandbox. Creating one adds database/configuration state and may require repeated capability or Connection provisioning, but compute is established for sessions when needed. Hundreds of workspaces are not inherently exceptional. Per-chat workspaces have more lifecycle and connector-management overhead, so automate reconciliation and deletion instead of weakening a hard privacy requirement.
20
+
21
+ ## Current session authority facts
22
+
23
+ - A top-level session created by an organization API key defaults to workspace visibility.
24
+ - Private or Only-me sessions require verified owning-user authority and organization activation. Native managed sessions and the server-side `asUser()` path establish that authority; a raw `endUser` payload does not.
25
+ - An agent must pass ordinary permissions and private-session ownership checks. The caller's `agentAccess` narrows outbound reach: `session` stays in its root tree; `user` requires matching non-null canonical scope users across trees; `workspace` adds no further restriction. The target's `agentAccess` never restricts inbound access. None of these modes overrides private visibility.
26
+ - Knowledge learning controls agent authoring. Turning it off leaves authorized retrieval available and does not remove session history, change session visibility, or neutralize cross-session tools.
27
+ - Hiding session-list and session-get alone is incomplete. Events, waiting, messaging, control, discovery, workspace Knowledge, documents, notes, or other workspace-wide tools may still cross the intended boundary.
28
+
29
+ One workspace per end user or One workspace per chat remains possible when the
30
+ resources and integration configuration themselves must be isolated, but is not
31
+ required merely to make a conversation private. Use the canonical private
32
+ session boundary for transcripts; choose separate workspaces for workspace
33
+ resources. Remove unnecessary tools as defense in depth, never as a substitute
34
+ for either boundary. Personal Knowledge follows the verified active-turn user; task
35
+ notes cover task-local coordination. Session-scoped Memory is retired without
36
+ promoting historical rows into workspace visibility.
37
+
38
+ ## Explicit headless tool policy
39
+
40
+ For a customer-facing headless session, never rely accidentally on omission:
41
+
42
+ - Omitting tools uses the workspace's configured MCP defaults; an explicit empty tools list suppresses them.
43
+ - Omitting firstPartyMcpTools selects the deployment's non-connector default catalog; an explicit empty list exposes none.
44
+ - Build an allowlist from the product's actual use case and the live SDK type or client configuration.
45
+ - Exclude cross-session tools unless collaboration is an explicit feature. Current examples include sessions_list, session_get, session_events, session_wait, session_send_message, session_pause, session_resume, session_steer, session_human_input_respond, set_other_session_title, and workspace-scoped discovery. Recheck the live catalog rather than treating this list as permanent.
46
+ - Also examine Knowledge, notes, files, artifacts, browsers, computers, scheduling, and capability-management tools. A tool is safe only when both its scope and its necessity fit the product.
47
+ - A tool allowlist narrows what the model can invoke; it does not repair an incorrectly shared workspace, an over-broad provider token, or a vulnerable customer API.
48
+
49
+ ## Backend mapping pattern
50
+
51
+ The product backend should:
52
+
53
+ 1. Authenticate the product request using the product's existing identity system.
54
+ 2. Derive the canonical sharing boundary from trusted server-side identity, such as tenant ID, user ID, or conversation ID.
55
+ 3. Resolve or lazily ensure the corresponding organization workspace with a stable externalSource plus externalId pair.
56
+ 4. Persist the returned opaque workspace ID with the product boundary record.
57
+ 5. Resolve the product's own session-to-OpenGeni-session mapping before every read, stream, message, control, or upload operation.
58
+ 6. Reject caller-supplied OpenGeni workspace or session IDs that do not match those mappings.
59
+
60
+ The externalId passed to `ensureWorkspace` identifies the product boundary; it does not create an OpenGeni human. A service-mode integration need not create one OpenGeni account or workspace membership per end user. External user mode is distinct: `asUser` lazily resolves an organization-scoped external identity, and shared access requires explicit membership intersected with the initiating key's permissions. See [External users and Connect](external-users-and-connect.md) for onboarding and current limitations. Provision workspaces lazily on first use, from a product lifecycle event, or through a controlled backfill according to operational needs. The ensure call is idempotent and should use the same identity on retries.
61
+
62
+ An organization API key is intentionally broad across organization workspaces. Keep it in the backend secret manager. Where a component needs only one workspace, consider a narrower workspace key. In either case the customer's backend remains responsible for mapping its authenticated principal to the correct OpenGeni boundary.
63
+
64
+ ## Isolation verification
65
+
66
+ Include negative tests, not only a successful chat:
67
+
68
+ - User A cannot open, stream, message, or attach a file to user B's mapped session through product routes.
69
+ - A manipulated browser request carrying another workspace or session ID is rejected before the OpenGeni call.
70
+ - A prompt that names or guesses another session cannot make the agent retrieve it with the selected tools.
71
+ - Workspaces created concurrently for the same boundary converge on one mapping; distinct boundary IDs never converge.
72
+ - Provider credentials and API tools cannot request another tenant merely by changing a request argument.
73
+ - Deleting or disabling a product user applies the customer's chosen session/workspace retention and access policy.
74
+
75
+ For same-workspace private sessions, verify ownership through HTTP, tools,
76
+ lists and streams; also verify optional agent reach independently. Test the
77
+ effective tool policy as defense in depth, not as proof of ownership enforcement.