@opengeni/sdk 3.7.0-canary.6 → 3.7.0-canary.7

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 (52) hide show
  1. package/README.md +84 -1
  2. package/dist/artifacts.js +4 -4
  3. package/dist/browser.js +2 -2
  4. package/dist/chat/fold.d.ts +46 -0
  5. package/dist/chat/handler.d.ts +39 -0
  6. package/dist/chat/http.d.ts +67 -0
  7. package/dist/chat/ids.d.ts +34 -0
  8. package/dist/chat/index.d.ts +7 -0
  9. package/dist/chat/index.js +1449 -0
  10. package/dist/chat/index.js.map +1 -0
  11. package/dist/chat/openai.d.ts +30 -0
  12. package/dist/chat/opengeni.d.ts +96 -0
  13. package/dist/chat/types.d.ts +142 -0
  14. package/dist/chat/vercel.d.ts +31 -0
  15. package/dist/{chunk-BFAP54AB.js → chunk-5PCW44P6.js} +2 -2
  16. package/dist/{chunk-O2L2LGWD.js → chunk-OOBHO7R7.js} +54 -8
  17. package/dist/chunk-OOBHO7R7.js.map +1 -0
  18. package/dist/{chunk-KKLG2S4L.js → chunk-UAXA2UKI.js} +133 -6
  19. package/dist/chunk-UAXA2UKI.js.map +1 -0
  20. package/dist/{chunk-IKOCZY4A.js → chunk-VQTDKVIG.js} +1 -1
  21. package/dist/chunk-VQTDKVIG.js.map +1 -0
  22. package/dist/{chunk-5EXQM7D3.js → chunk-XOTTWRJJ.js} +2 -2
  23. package/dist/client.d.ts +69 -2
  24. package/dist/core.js +3 -3
  25. package/dist/document-authority.js +3 -3
  26. package/dist/editable-artifacts.js +1 -1
  27. package/dist/feedback.d.ts +23 -0
  28. package/dist/index.d.ts +3 -1
  29. package/dist/index.js +5 -5
  30. package/dist/model-context.d.ts +12 -1
  31. package/dist/site.js +3 -3
  32. package/dist/types.d.ts +89 -5
  33. package/package.json +6 -2
  34. package/src/chat/fold.ts +281 -0
  35. package/src/chat/handler.ts +236 -0
  36. package/src/chat/http.ts +257 -0
  37. package/src/chat/ids.ts +85 -0
  38. package/src/chat/index.ts +77 -0
  39. package/src/chat/openai.ts +439 -0
  40. package/src/chat/opengeni.ts +517 -0
  41. package/src/chat/types.ts +153 -0
  42. package/src/chat/vercel.ts +173 -0
  43. package/src/client.ts +209 -5
  44. package/src/feedback.ts +20 -0
  45. package/src/index.ts +11 -0
  46. package/src/model-context.ts +14 -0
  47. package/src/types.ts +90 -4
  48. package/dist/chunk-IKOCZY4A.js.map +0 -1
  49. package/dist/chunk-KKLG2S4L.js.map +0 -1
  50. package/dist/chunk-O2L2LGWD.js.map +0 -1
  51. /package/dist/{chunk-BFAP54AB.js.map → chunk-5PCW44P6.js.map} +0 -0
  52. /package/dist/{chunk-5EXQM7D3.js.map → chunk-XOTTWRJJ.js.map} +0 -0
package/README.md CHANGED
@@ -18,6 +18,82 @@ bearer design, but an organization API key belongs on the product server.
18
18
  Browser cookies are accepted cross-origin only from operator-configured trusted
19
19
  origins; arbitrary embedding origins never receive credentialed CORS responses.
20
20
 
21
+ ## Chat quick start (`@opengeni/sdk/chat`)
22
+
23
+ The fastest way to put OpenGeni behind an existing chat: one option object per
24
+ conversation, one server handler for your endpoint. Tenants map to organization
25
+ workspaces, conversations map to deterministic sessions, and the organization
26
+ API key never leaves your server.
27
+
28
+ ```ts
29
+ import { OpenGeni, createChatHandler } from "@opengeni/sdk/chat";
30
+
31
+ const og = new OpenGeni({
32
+ apiKey: process.env.OPENGENI_API_KEY!,
33
+ organizationId: process.env.OPENGENI_ORGANIZATION_ID!,
34
+ // baseUrl defaults to https://app.opengeni.ai; source (default "app") labels your product.
35
+ });
36
+
37
+ const chat = await og.chat({
38
+ tenant: "acme", // one workspace per customer, created on first use
39
+ user: "u_42", // opaque end-user label (required for memory: "user")
40
+ conversation: "c_9", // stable id, namespaced to user; the session id is derived from both
41
+ agentAccess: "session", // "session" (default) | "user" | "workspace"
42
+ memory: "user", // "session" | "user" | "workspace" | false; default follows agentAccess
43
+ create: { sandboxBackend: "none" }, // raw create-request passthrough for a pure chat
44
+ });
45
+
46
+ const reply = await chat.send("hello"); // creates the session on the first send
47
+ console.log(reply.text); // or String(reply)
48
+
49
+ for await (const chunk of chat.stream("and then?")) {
50
+ if (chunk.type === "text") process.stdout.write(chunk.text);
51
+ if (chunk.type === "pending") await chat.respond({ requestId: chunk.pending.requestId, decision: "approve" });
52
+ }
53
+
54
+ // Your endpoint. `resolve` is your auth hook: identity comes from the request
55
+ // you authenticated, never from the body. The handler reads the client's
56
+ // conversation id itself (x-opengeni-conversation header, or the wire format's
57
+ // own field) and scopes it to `user`; return `conversation` from resolve only
58
+ // when the host names it, which is required when there is no `user`.
59
+ export const handler = createChatHandler(og, {
60
+ resolve: async (request) => {
61
+ const session = await getSessionFromCookie(request);
62
+ if (!session) return new Response("Unauthorized", { status: 401 });
63
+ return { tenant: session.accountId, user: session.userId };
64
+ },
65
+ // format: "vercel" | "openai-chat" | "openai-responses" (default "native");
66
+ // a request may override it with the x-opengeni-chat-format header.
67
+ });
68
+ export const GET = handler; // conversation history, for restoring the chat on reload
69
+ export const POST = handler; // send a message, or answer a pending request at .../respond
70
+ ```
71
+
72
+ Conversation ids are namespaced per user: the same `conversation` for two
73
+ `user` labels reaches two sessions, so a client cannot continue another user's
74
+ chat by guessing its id. Without a `user`, the host must name the conversation
75
+ from `resolve`. The Vercel and OpenAI adapters send only the latest user
76
+ message; the earlier messages in that request are imported once, as context on
77
+ the first message of a conversation, after which OpenGeni owns the history.
78
+
79
+ Pick the isolation per session with `agentAccess` (which other sessions the
80
+ agent may reach) and `memory` (what it remembers), all inside one workspace that
81
+ shares the customer's documents, instructions, and integrations:
82
+
83
+ | Scenario | `agentAccess` | `memory` |
84
+ | ------------------------------------------------- | ------------- | ------------- |
85
+ | Every chat isolated (support desk) | `"session"` | `"session"` |
86
+ | One user's chats see each other, not other users' | `"user"` | `"user"` |
87
+ | Everything in the tenant shared | `"workspace"` | `"workspace"` |
88
+ | Shared agent access, no memory | any | `false` |
89
+
90
+ `<OpenGeniChat handlerUrl="/api/chat" conversation="c_9" />` from
91
+ `@opengeni/react/chat` is the browser component to swap in for your chat box:
92
+ it talks only to your handler, sends the conversation id as the
93
+ `x-opengeni-conversation` header, and restores the history on reload. Graduate
94
+ to `OpenGeniClient` below when you need the full session surface: files, tools,
95
+ approvals with policies, forks, realtime voice.
96
+
21
97
  ## Quick start
22
98
 
23
99
  ```ts
@@ -76,7 +152,14 @@ complete organization-workspace inventory.
76
152
  Organization key administration uses `listOrganizationApiKeys`,
77
153
  `createOrganizationApiKey`, and `deleteOrganizationApiKey`. The key token from a
78
154
  create response is shown once and must be stored in the backend's secret
79
- manager.
155
+ manager. `createOrganizationApiKey(organizationId, { name, access: "read" })`
156
+ mints a read-only master key: it inventories shared workspaces and reads their
157
+ sessions, events, and files, but cannot create sessions, send messages, or mint
158
+ keys, and every key reports its tier as `apiKey.access`. Either tier can call
159
+ `listOrganizationSessions(organizationId, { limit, cursor, endUser, status })`
160
+ for one page of sessions across every shared workspace (each row carries its
161
+ `workspaceId`; private sessions and Personal workspaces never appear), or
162
+ `iterateOrganizationSessions` to follow `nextCursor` to the end.
80
163
 
81
164
  The external backend also owns its Skill catalog. Load the selected definitions
82
165
  and pass them inline in `CreateSessionRequest.skills` for each product-created
package/dist/artifacts.js CHANGED
@@ -1,10 +1,10 @@
1
1
  import {
2
2
  OpenGeniClient
3
- } from "./chunk-BFAP54AB.js";
4
- import "./chunk-5EXQM7D3.js";
5
- import "./chunk-KKLG2S4L.js";
3
+ } from "./chunk-5PCW44P6.js";
4
+ import "./chunk-XOTTWRJJ.js";
5
+ import "./chunk-UAXA2UKI.js";
6
6
  import "./chunk-QTBAMHEF.js";
7
- import "./chunk-IKOCZY4A.js";
7
+ import "./chunk-VQTDKVIG.js";
8
8
  import "./chunk-OO5LPTO7.js";
9
9
  export {
10
10
  OpenGeniClient
package/dist/browser.js CHANGED
@@ -8,12 +8,12 @@ import {
8
8
  } from "./chunk-FMLDFJ2Q.js";
9
9
  import {
10
10
  OpenGeniClient
11
- } from "./chunk-KKLG2S4L.js";
11
+ } from "./chunk-UAXA2UKI.js";
12
12
  import "./chunk-QTBAMHEF.js";
13
13
  import {
14
14
  OPENGENI_API_CONTRACT_HEADER,
15
15
  OPENGENI_API_CONTRACT_REVISION
16
- } from "./chunk-IKOCZY4A.js";
16
+ } from "./chunk-VQTDKVIG.js";
17
17
  import {
18
18
  OpenGeniApiContractMismatchError,
19
19
  OpenGeniApiError,
@@ -0,0 +1,46 @@
1
+ import type { SessionEvent } from "../types.js";
2
+ import { OpenGeniChatError, type ChatChunk, type ChatPending, type ChatReply } from "./types.js";
3
+ /**
4
+ * Event folding shared by `send`, `stream`, and every protocol adapter.
5
+ *
6
+ * One fold covers one logical turn: text deltas accumulate into message
7
+ * segments (a tool call closes the current segment, the completed event
8
+ * reconciles it), tool call/output pairs become tool chunks, and the turn
9
+ * settles on a terminal event or a human wait. Events that carry a different
10
+ * turn id than the expected one are ignored so a queued follow-up never ends
11
+ * early on the previous turn's settlement.
12
+ */
13
+ export type ChatTurnTerminal = "completed" | "failed" | "cancelled" | "pending";
14
+ export type ChatFoldStep = {
15
+ chunks: ChatChunk[];
16
+ terminal: ChatTurnTerminal | null;
17
+ };
18
+ /** Incremental replay of unresolved requests, without retaining the event log. */
19
+ export declare class ChatPendingFold {
20
+ private readonly requests;
21
+ push(event: SessionEvent): void;
22
+ pending(now?: number): ChatPending[];
23
+ private put;
24
+ }
25
+ export declare class ChatTurnFold {
26
+ private readonly workspaceId;
27
+ private readonly sessionId;
28
+ readonly events: SessionEvent[];
29
+ turnId: string | null;
30
+ pending: ChatPending | null;
31
+ failure: SessionEvent | null;
32
+ private readonly segments;
33
+ private readonly openTools;
34
+ constructor(workspaceId: string, sessionId: string, expectedTurnId: string | null);
35
+ get text(): string;
36
+ push(event: SessionEvent): ChatFoldStep;
37
+ reply(terminal: ChatTurnTerminal): ChatReply;
38
+ /** The error to throw for a `turn.failed` settlement. */
39
+ failureError(): OpenGeniChatError;
40
+ private closeSegment;
41
+ private startSegment;
42
+ }
43
+ export declare function approvalPending(payload: Record<string, unknown>): ChatPending | null;
44
+ export declare function humanInputPending(payload: Record<string, unknown>): ChatPending | null;
45
+ export declare function asRecord(value: unknown): Record<string, unknown>;
46
+ export declare function stringValue(value: unknown): string | undefined;
@@ -0,0 +1,39 @@
1
+ import { type ChatResolve } from "./http.js";
2
+ import type { OpenGeni } from "./opengeni.js";
3
+ import { type ChatChunk, type ChatRespondInput } from "./types.js";
4
+ export { CHAT_CONVERSATION_HEADER, type ChatResolution, type ChatResolve } from "./http.js";
5
+ export type ChatHandlerFormat = "native" | "vercel" | "openai-chat" | "openai-responses";
6
+ export type ChatHandlerOptions = {
7
+ /** Mandatory host auth hook; see {@link ChatResolve}. */
8
+ resolve: ChatResolve;
9
+ /** Default wire format; a request may override it with the format header. */
10
+ format?: ChatHandlerFormat | undefined;
11
+ };
12
+ /** Per-request wire-format override header. */
13
+ export declare const CHAT_FORMAT_HEADER = "x-opengeni-chat-format";
14
+ /**
15
+ * One request handler for a product's chat endpoint. `POST` with `{ message }`
16
+ * streams the reply in the selected format; `POST .../respond` answers a
17
+ * pending approval or human-input request and streams the continuation;
18
+ * `GET` returns the conversation's history as JSON. Every other method is a
19
+ * 405. The conversation is the host's resolution, else the
20
+ * `x-opengeni-conversation` header, else the wire format's own field.
21
+ */
22
+ export declare function createChatHandler(og: OpenGeni, options: ChatHandlerOptions): (request: Request) => Promise<Response>;
23
+ /** `POST { message }` -> native SSE of {@link ChatChunk} (`event: chunk`). */
24
+ export declare function handleNativeChatRequest(og: OpenGeni, request: Request, resolve: ChatResolve): Promise<Response>;
25
+ /**
26
+ * `GET` -> `{ conversation, sessionId, created, messages, pending, status }`: the user and
27
+ * assistant text of the conversation so a client can restore it on reload
28
+ * (`messages` is empty and `created` false before the first message).
29
+ */
30
+ export declare function handleNativeHistoryRequest(og: OpenGeni, request: Request, resolve: ChatResolve): Promise<Response>;
31
+ /** `POST .../respond { requestId, decision | answers | skip }` -> native SSE of the continuation. */
32
+ export declare function handleNativeRespondRequest(og: OpenGeni, request: Request, resolve: ChatResolve): Promise<Response>;
33
+ export declare function respondInputFromBody(body: Record<string, unknown> | null): ChatRespondInput | null;
34
+ /** Native wire format: `event: chunk` per {@link ChatChunk}, `event: error` on failure. */
35
+ export declare function chatChunksToSseBlocks(chunks: AsyncIterable<ChatChunk>): AsyncGenerator<string, void, void>;
36
+ export declare function chatChunksToSseStream(chunks: AsyncIterable<ChatChunk>, onCancel?: () => void): ReadableStream<Uint8Array>;
37
+ export declare function chatChunksToSseResponse(chunks: AsyncIterable<ChatChunk>): Response;
38
+ /** Browser-side reader for the native wire format. Throws {@link OpenGeniChatError} on `event: error`. */
39
+ export declare function parseChatChunkStream(stream: ReadableStream<Uint8Array>): AsyncGenerator<ChatChunk, void, void>;
@@ -0,0 +1,67 @@
1
+ import type { OpenGeni } from "./opengeni.js";
2
+ import { type ChatImportedMessage, type ChatOptions } from "./types.js";
3
+ /** Internal HTTP plumbing shared by the native handler and the protocol adapters. */
4
+ /**
5
+ * Header a client uses to name the conversation it is on. Every handler reads
6
+ * the client conversation as: the host's resolution, else this header, else
7
+ * the protocol's own field.
8
+ */
9
+ export declare const CHAT_CONVERSATION_HEADER = "x-opengeni-conversation";
10
+ /** What the host's auth hook returns: identity from the host, conversation optional per protocol. */
11
+ export type ChatResolution = Omit<ChatOptions, "conversation"> & {
12
+ conversation?: string | undefined;
13
+ };
14
+ /**
15
+ * The host's authentication hook. It receives the raw request and returns the
16
+ * tenant/user/conversation the caller is allowed to use, or a `Response` to
17
+ * reject the request. Never derive tenant or user from the request body.
18
+ */
19
+ export type ChatResolve = (request: Request) => Promise<ChatResolution | Response> | ChatResolution | Response;
20
+ export type ChatErrorSummary = {
21
+ status: number;
22
+ code: string;
23
+ message: string;
24
+ };
25
+ export declare function jsonResponse(body: unknown, status?: number, headers?: HeadersInit): Response;
26
+ export declare function errorResponse(status: number, message: string, code: string): Response;
27
+ export declare function readJsonObject(request: Request): Promise<Record<string, unknown> | null>;
28
+ export declare function resolveChatRequest(request: Request, resolve: ChatResolve): Promise<{
29
+ resolution: ChatResolution;
30
+ response?: undefined;
31
+ } | {
32
+ response: Response;
33
+ }>;
34
+ /** The client-supplied conversation: the header, else the protocol's own field. */
35
+ export declare function clientConversation(request: Request, protocolField: unknown): string | undefined;
36
+ /**
37
+ * Open a chat from a resolution plus the conversation the client supplied.
38
+ * The host names the conversation, or the host names the user and the client
39
+ * conversation id is namespaced to that user; anything else is a 400, because
40
+ * an unscoped client id could address any conversation in the workspace.
41
+ */
42
+ export declare function openResolvedChat(og: OpenGeni, resolution: ChatResolution, clientConversationId: string | undefined): Promise<{
43
+ chat: Awaited<ReturnType<OpenGeni["chat"]>>;
44
+ response?: undefined;
45
+ } | {
46
+ response: Response;
47
+ }>;
48
+ export declare function chatErrorSummary(error: unknown): ChatErrorSummary;
49
+ export declare function sseHeaders(extra?: Record<string, string>): Record<string, string>;
50
+ export declare function sseLine(data: string, event?: string): string;
51
+ /**
52
+ * Pull-based text-to-bytes stream over already-formatted SSE blocks. Upstream
53
+ * consumption follows downstream demand; cancelling fires `onCancel` so the
54
+ * producer can abort its OpenGeni stream.
55
+ */
56
+ export declare function sseByteStream(blocks: AsyncIterable<string>, onCancel?: () => void): ReadableStream<Uint8Array>;
57
+ /** Text from an OpenAI-style message content: a string or `[{ type, text }]` parts. */
58
+ export declare function messageContentText(content: unknown, partTypes: string[]): string | null;
59
+ /** The last user-role message's text from an OpenAI/Vercel-style `messages` array. */
60
+ export declare function lastUserMessageText(messages: unknown, partTypes: string[], partsField: "parts" | "content"): string | null;
61
+ /**
62
+ * Every user/assistant/system message before the last user message, as
63
+ * imported history for the first create. Items without a role or text
64
+ * (tool calls, files) are skipped.
65
+ */
66
+ export declare function importedHistoryBefore(messages: unknown, partTypes: string[], partsField: "parts" | "content"): ChatImportedMessage[];
67
+ export declare function unixSeconds(): number;
@@ -0,0 +1,34 @@
1
+ /**
2
+ * Deterministic chat identities. A conversation id maps to exactly one
3
+ * session id per workspace, so any process can address the same session
4
+ * without storing a mapping, and a double-submitted create collapses through
5
+ * the idempotency key. With an end-user label the conversation id is
6
+ * namespaced to that user, so one user cannot address another user's
7
+ * conversation by guessing its id.
8
+ */
9
+ /** The end-user label a conversation is namespaced to: the product `source` plus the opaque user id. */
10
+ export type ChatUserLabel = {
11
+ source: string;
12
+ id: string;
13
+ };
14
+ /** Fixed RFC 4122 namespace for chat session ids. Never change it. */
15
+ export declare const CHAT_SESSION_NAMESPACE = "7c1e6d3a-5b2f-4e8a-9d4c-0f3b6a8e2c17";
16
+ /** RFC 4122 version 5 (SHA-1) UUID of `name` inside `namespace`. */
17
+ export declare function uuidV5(name: string, namespace: string): Promise<string>;
18
+ /**
19
+ * The session id of `conversation` in `workspaceId`: RFC 4122 v5 of the JSON
20
+ * tuple `[workspaceId, conversation]`, or `[workspaceId, source, id, conversation]`
21
+ * when the conversation belongs to an end user. JSON encoding keeps the tuple
22
+ * unambiguous: an id containing `:` or any other delimiter can never make two
23
+ * different (user, conversation) pairs share a session.
24
+ */
25
+ export declare function chatSessionId(workspaceId: string, conversation: string, user?: ChatUserLabel | undefined): Promise<string>;
26
+ /** The exact v5 name behind {@link chatSessionId}; exported for tests and audits. */
27
+ export declare function chatIdentityName(workspaceId: string, conversation: string, user?: ChatUserLabel | undefined): string;
28
+ /**
29
+ * The create idempotency key for a chat session: `chat:<sessionId>`. The
30
+ * session id already encodes the workspace, user label, and conversation as an
31
+ * unambiguous tuple, so the key inherits that and stays bounded.
32
+ */
33
+ export declare function chatIdempotencyKey(sessionId: string): string;
34
+ export declare function isUuid(value: string): boolean;
@@ -0,0 +1,7 @@
1
+ export { Chat, DEFAULT_CHAT_SOURCE, DEFAULT_OPENGENI_BASE_URL, OpenGeni } from "./opengeni.js";
2
+ export { OpenGeniChatError, type ChatAgentAccess, type ChatChunk, type ChatImportedMessage, type ChatMemory, type ChatMessage, type ChatOptions, type ChatPending, type ChatReply, type ChatReplyStatus, type ChatRespondInput, type ChatSendOptions, type ChatSessionListOptions, type ChatSnapshot, type ChatTarget, type ChatToolStatus, type OpenGeniOptions, } from "./types.js";
3
+ export { CHAT_SESSION_NAMESPACE, chatIdempotencyKey, chatIdentityName, chatSessionId, uuidV5, type ChatUserLabel, } from "./ids.js";
4
+ export { ChatTurnFold, approvalPending, humanInputPending, type ChatFoldStep, type ChatTurnTerminal, } from "./fold.js";
5
+ export { CHAT_CONVERSATION_HEADER, CHAT_FORMAT_HEADER, chatChunksToSseBlocks, chatChunksToSseResponse, chatChunksToSseStream, createChatHandler, handleNativeChatRequest, handleNativeHistoryRequest, handleNativeRespondRequest, parseChatChunkStream, respondInputFromBody, type ChatHandlerFormat, type ChatHandlerOptions, type ChatResolution, type ChatResolve, } from "./handler.js";
6
+ export { UI_MESSAGE_STREAM_HEADER, UI_MESSAGE_STREAM_VERSION, chatChunksToUIMessageStream, handleVercelChatRequest, lastUIMessageText, uiMessageStreamBlocks, uiMessageStreamParts, uiMessageStreamResponse, type UIMessageStreamOptions, } from "./vercel.js";
7
+ export { chatCompletionBlocks, chatCompletionObject, decodeResponseId, encodeResponseId, handleChatCompletionsRequest, handleResponsesRequest, responsesBlocks, responsesInputText, } from "./openai.js";