@oberik/sdk 0.1.0 → 0.2.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.
@@ -82,6 +82,8 @@ export interface ClientOptions {
82
82
  headers?: Record<string, string>;
83
83
  /** Client-side tools registered up-front; auto-dispatched by chat.run/stream. */
84
84
  tools?: ClientToolDef[];
85
+ /** UI components the agent can draw into your app. Needs the `ui_tools` capability. */
86
+ ui?: UiComponent<any>[];
85
87
  }
86
88
  /** Public tenant view — never includes the API key. */
87
89
  export interface TenantOut {
@@ -540,10 +542,86 @@ export interface QuestionAnswer {
540
542
  }
541
543
  /** Answer a batch of questions. Return one entry per question (or a single
542
544
  * `{ chat_instead: true }` to decline the whole batch and keep chatting). */
545
+ /** An Agent Plugin visible to this token: what the project published, plus anything
546
+ * this end-user uploaded. */
547
+ export interface PublishedPlugin {
548
+ id: string;
549
+ name: string;
550
+ version?: string | null;
551
+ description: string;
552
+ /** `tenant` = published by the project to everyone; `private` = this user's own. */
553
+ visibility: string;
554
+ owner_subject?: string | null;
555
+ enabled: boolean;
556
+ size_bytes: number;
557
+ /** Each skill's name and description are all the agent sees until it loads one, so
558
+ * the description is the line that decides whether a procedure ever gets used. */
559
+ skills: {
560
+ name: string;
561
+ description: string;
562
+ files: string[];
563
+ }[];
564
+ mcp_servers: string[];
565
+ /** Wrong but not fatal — a skill that would not parse, a server that was refused.
566
+ * Worth surfacing: a plugin whose skill silently did not load is the failure people
567
+ * spend an afternoon on. */
568
+ warnings: string[];
569
+ }
570
+ /** A component your app can render, declared for the agent to call.
571
+ *
572
+ * The difference from a client tool is that there is no result. A client tool is work
573
+ * handed back — the turn stops, you run it, the answer returns. This is a one-way
574
+ * instruction: the agent calls it, your `render` runs, and the turn carries straight
575
+ * on. Use it wherever showing beats describing. */
576
+ export interface UiComponent<A = Record<string, unknown>> {
577
+ name: string;
578
+ /** What it shows, and when the agent should reach for it. The agent picks from this,
579
+ * so "Draw a line chart of a numeric series over time" beats "chart". */
580
+ description: string;
581
+ /** JSON Schema for the arguments, exactly as a client tool declares them. */
582
+ parameters?: Record<string, unknown>;
583
+ /** Called when the agent draws it. Return nothing — anything you return is dropped,
584
+ * because the agent is not waiting. */
585
+ render: (args: A) => void;
586
+ }
587
+ /** One thing the agent drew, as it comes back on the turn. */
588
+ export interface UiRender {
589
+ name: string;
590
+ args: Record<string, unknown>;
591
+ }
592
+ /** Something the agent stopped to ask permission for.
593
+ *
594
+ * Arrives with `requires_action` and an EMPTY `tool_calls`: an approval is not the
595
+ * client's work to execute, so tool auto-dispatch can never grant one on the user's
596
+ * behalf. It has to reach a person. */
597
+ export interface PendingApproval {
598
+ tool_call_id: string;
599
+ /** One line saying exactly what the agent will do, with the specifics in it. */
600
+ action: string;
601
+ /** What the person needs in order to decide — the message, the command, the rows. */
602
+ detail?: string | null;
603
+ /** What cannot easily be undone. Usually the only part that decides the answer. */
604
+ consequence?: string | null;
605
+ }
606
+ /** A person's answer to one approval pause. */
607
+ export interface ApprovalDecision {
608
+ tool_call_id?: string;
609
+ approved: boolean;
610
+ /** The difference between "no" and "no, use the other address". */
611
+ note?: string | null;
612
+ }
613
+ /** Put the request to a person and return their decision.
614
+ *
615
+ * Throwing, or returning `false`, is a refusal — and a refusal stops the work: the
616
+ * agent is told not to try another way and to ask what you want instead. */
617
+ export type ApprovalHandler = (request: PendingApproval) => ApprovalDecision | boolean | Promise<ApprovalDecision | boolean>;
543
618
  export type QuestionHandler = (pending: PendingQuestions) => QuestionAnswer | QuestionAnswerItem[] | Promise<QuestionAnswer | QuestionAnswerItem[]>;
544
619
  export interface RunOptions extends Omit<ChatRequest, "tool_results" | "client_tools"> {
545
620
  /** Extra client tools for this call (merged over any registered on the client). */
546
621
  tools?: ClientToolDef[];
622
+ /** UI components for this call (merged over any registered on the client). Needs the
623
+ * `ui_tools` capability. */
624
+ ui?: UiComponent<any>[];
547
625
  /** Max auto tool-dispatch rounds before giving up (default 10). */
548
626
  maxToolRounds?: number;
549
627
  /** Fires before each batch of client tools is executed. */
@@ -551,10 +629,24 @@ export interface RunOptions extends Omit<ChatRequest, "tool_results" | "client_t
551
629
  /** Answer the agent's questions and continue the turn automatically. Without it,
552
630
  * `run` returns as soon as the agent asks, with `questions` set. */
553
631
  onQuestion?: QuestionHandler;
632
+ /** Decide the agent's approval requests and continue the turn. Without it, `run`
633
+ * returns as soon as the agent asks, with `approvals` set — which is the right
634
+ * default: nothing should be able to approve an irreversible action by accident. */
635
+ onApproval?: ApprovalHandler;
554
636
  signal?: AbortSignal;
555
637
  }
556
- /** A non-text input part (any modality the model supports). `url` is a data: URI or
557
- * an https URL. Gated by the token's `input:<kind>` capability. */
638
+ /** A non-text input part. `url` is a data: URI or an https URL.
639
+ *
640
+ * Two capabilities decide what happens to it, and they are separate on purpose:
641
+ *
642
+ * - `input:file` accepts ANY attachment — a PNG and a WAV included. What the model
643
+ * cannot perceive is read as text instead (OCR for a scan, a transcript, an
644
+ * extraction), so granting it costs fidelity, never access.
645
+ * - `input:image` / `input:audio` / `input:video` additionally let the model look at
646
+ * that kind with its own senses, raw.
647
+ *
648
+ * So a project that just wants users to attach whatever they have grants `input:file`;
649
+ * one that wants the model to actually SEE the picture grants `input:image` too. */
558
650
  export interface Attachment {
559
651
  /** Stable identity, set on anything the agent produced. Match on this, not on `url`:
560
652
  * a URL is signed per response, so the same file arrives with a different one from
@@ -623,14 +715,39 @@ export interface ChatRequest {
623
715
  messages?: string[] | null;
624
716
  /** Multimodal inputs (images/audio/video/files) for this turn. */
625
717
  attachments?: Attachment[] | null;
626
- /** Modalities the model may return, e.g. ["text","image"]. Bounded by the token's
627
- * output:<modality> capabilities; ignored by text-only models. */
718
+ /** Modalities the MODEL may generate this turn, e.g. ["text","image"]. Bounded by the
719
+ * token's `output:<modality>` capabilities; ignored by text-only models.
720
+ *
721
+ * Nothing to do with what the agent may hand over as a file: a screenshot it took or
722
+ * a chart it rendered in a sandbox is a file it produced, not a modality it emitted,
723
+ * and `output:file` is what permits sending those — any of them, pictures included. */
628
724
  output_modalities?: string[] | null;
629
725
  tool_results?: ClientToolResult[] | null;
630
726
  /** Resume a turn paused on `ask_user`. The wording the agent reads is composed
631
727
  * server-side from the question it actually asked, so the transcript can't drift
632
728
  * from what was on screen. */
633
729
  question_answers?: QuestionAnswer[] | null;
730
+ /** UI components the agent may draw this turn. `chat.run`/`chat.stream` fill this in
731
+ * from the components you registered — set it directly only on a raw `/chat` call. */
732
+ ui_tools?: Record<string, unknown>[] | null;
733
+ /** Answers to approval pauses, resuming the turn. */
734
+ approval_decisions?: ApprovalDecision[] | null;
735
+ /** Let the agent stop and ask permission before an action with consequences. Needs
736
+ * the `approvals` capability. Set false for any caller that cannot answer — a batch
737
+ * job, a webhook — so the agent never stalls waiting on a decision. */
738
+ enable_approvals?: boolean;
739
+ /** Let the agent reach for the skills published to this project. Needs the `plugins`
740
+ * capability. */
741
+ /** Let the agent call the tools this project publishes as URLs — Oberik POSTs to
742
+ * the customer's endpoint with the end-user's identity signed into the body. Needs
743
+ * the `webhook_tools` capability. Turn it off for a turn that should not touch your
744
+ * systems. */
745
+ enable_webhook_tools?: boolean;
746
+ enable_plugins?: boolean;
747
+ /** Which plugins this turn may use, by name. Omit for everything the token can see.
748
+ * Narrowing only — a name that is not visible is absent rather than an error, so a
749
+ * pinned list does not start failing turns the day someone deletes one. */
750
+ plugins?: string[] | null;
634
751
  /** The user pressed Done on a page the agent handed them. Resumes a turn that
635
752
  * stopped on a blocking hand-off — the button IS the answer, so nothing else is
636
753
  * needed with it. */
@@ -662,7 +779,6 @@ export interface ChatRequest {
662
779
  * Omitted = the sandbox bound to this chat session, else a fresh one. */
663
780
  computer_session_id?: string | null;
664
781
  /** Let the agent run read-only SQL against connected query-mode data sources. */
665
- enable_data_query?: boolean;
666
782
  /** Let the agent drive a real browser — click, type, scroll, wait, capture — rather
667
783
  * than fetching one page at a time. Needs the `browser` capability and the browser
668
784
  * service; `web_search`/`browse_url` work without it. */
@@ -701,10 +817,9 @@ export interface Citation {
701
817
  quote: string | null;
702
818
  }
703
819
  /** A unified source for UI rendering (clickable pill) — a document citation, a
704
- * web result/page, or a database query. `type` = "document" | "web" | "query"
705
- * (query sources carry the executed SQL in `snippet` for verifiability). */
820
+ * web result/page. `type` = "document" | "web". */
706
821
  export interface Source {
707
- type: "document" | "web" | "query" | string;
822
+ type: "document" | "web" | string;
708
823
  title?: string | null;
709
824
  url?: string | null;
710
825
  snippet?: string | null;
@@ -723,6 +838,10 @@ export interface ChatResponse {
723
838
  * auto-dispatch can never answer it on the user's behalf. Resume by sending
724
839
  * `question_answers` (or let `chat.run`/`chat.stream` do it via `onQuestion`). */
725
840
  questions: PendingQuestions[];
841
+ /** The agent stopped before doing something and is waiting to be allowed. Same shape
842
+ * of pause as `questions`, and likewise never in `tool_calls`. Resume by sending
843
+ * `approval_decisions` (or let `chat.run`/`chat.stream` do it via `onApproval`). */
844
+ approvals: PendingApproval[];
726
845
  /** The agent's plan for this session, when the todo family ran this turn. */
727
846
  todos: TodoItem[];
728
847
  /** Every subagent of this conversation, including ones still working — a turn ending
@@ -735,6 +854,10 @@ export interface ChatResponse {
735
854
  /** Non-text outputs of the turn: generated media, and files the agent exported
736
855
  * from its sandbox. `url` is signed fresh per response — don't persist it. */
737
856
  attachments: Attachment[];
857
+ /** Components the agent drew this turn, in call order. `chat.run`/`chat.stream` have
858
+ * already called each component's `render` by the time you see this; it is here so a
859
+ * client that re-mounts can redraw from the list. */
860
+ ui: UiRender[];
738
861
  /** Guardrail actions taken this turn, e.g. "pii:EMAIL", "ungrounded", "injection". */
739
862
  guard_flags: string[];
740
863
  /** A reasoning model's thinking for this turn, else "". Live only — it is not stored
@@ -797,42 +920,63 @@ export interface TaskOut {
797
920
  last_error: string | null;
798
921
  created_at: string;
799
922
  }
800
- export type ConnectorType = "postgres" | "http" | string;
801
- export interface SyncConfig {
923
+ /** What a project key may do. An admin key satisfies a mint requirement, never the
924
+ * reverse. */
925
+ export type ProjectKeyScope = "admin" | "mint";
926
+ export interface ProjectKeyInfo {
927
+ id: string;
802
928
  name: string;
803
- connector_type: ConnectorType;
804
- /**
805
- * "sync" (default) copies records into the vector store (RAG over the data).
806
- * "query" is live query-in-place: the agent runs read-only SQL against the source
807
- * at question time — the right mode for structured/analytical data (postgres only).
808
- */
809
- mode?: "sync" | "query";
810
- /** Connector-specific config (dsn/query, token/repo, ...). Holds secrets. */
811
- config: Record<string, unknown>;
812
- /** Auto-refresh cadence in seconds; 0 = manual only. Default 3600. Ignored for query mode. */
813
- sync_interval_seconds?: number;
814
- tags?: string[];
815
- visibility?: "self" | "private" | "shared" | "groups" | "tenant";
816
- visibility_scope?: string | null;
817
- acl_roles?: string[];
818
- acl_groups?: string[];
929
+ scope: ProjectKeyScope;
930
+ /** The visible stub, e.g. "pk_ab12…". The key itself is shown once, at creation. */
931
+ prefix: string;
932
+ lastUsedAt: string | null;
933
+ createdAt: string;
934
+ }
935
+ /** What a token holds, and which per-turn switches are worth setting on it. */
936
+ export interface TokenCapabilities {
937
+ subject: string;
938
+ scope: string;
939
+ /** `null` = unrestricted (a service key), which is NOT the same as an empty list. */
940
+ capabilities: string[] | null;
941
+ flags: {
942
+ flag: string;
943
+ /** Any one of these is enough to grant it. Empty = ungated. */
944
+ capability: string[];
945
+ granted: boolean;
946
+ platform_enabled: boolean;
947
+ /** Whether setting this flag on a request would change anything. */
948
+ effective: boolean;
949
+ label: string;
950
+ description: string;
951
+ group: string | null;
952
+ }[];
953
+ modalities: {
954
+ /** What may be attached. */
955
+ input: string[];
956
+ /** What the model perceives directly; the rest arrives as extracted text. */
957
+ native: string[];
958
+ /** What the agent may hand back. */
959
+ output: string[];
960
+ };
961
+ limits: {
962
+ max_effort: string | null;
963
+ max_tool_iterations: number | null;
964
+ max_context_tokens: number | null;
965
+ };
819
966
  }
820
- export interface SourceOut {
967
+ /** One memory or wiki page. */
968
+ export interface KnowledgeItem {
821
969
  id: string;
822
- name: string;
823
- connector_type: ConnectorType;
824
- mode: "sync" | "query";
825
- /** Config key names only — secret values are never returned. */
826
- config_keys: string[];
827
- sync_interval_seconds: number;
828
- tags: string[];
970
+ kind: "memory" | "wiki" | string;
971
+ title?: string | null;
972
+ text: string;
973
+ owner_subject?: string | null;
829
974
  visibility: string;
830
- visibility_scope: string | null;
831
- status: string;
832
- error: string | null;
833
- record_count: number;
834
- last_synced_at: string | null;
835
- created_at: string;
975
+ /** Documents a wiki page was written from. */
976
+ source_document_ids: string[];
977
+ written_at?: string | null;
978
+ /** True once a source document changed or went away — the page is unverified. */
979
+ stale?: boolean;
836
980
  }
837
981
  export interface AuditEntry {
838
982
  id: string;
@@ -960,6 +1104,19 @@ export type StreamEvent = {
960
1104
  questions: PendingQuestions[];
961
1105
  };
962
1106
  }
1107
+ /** The agent is waiting to be allowed to do something. Render it and answer — nothing
1108
+ * proceeds until you do. */
1109
+ | {
1110
+ event: "approvals";
1111
+ data: {
1112
+ approvals: PendingApproval[];
1113
+ };
1114
+ }
1115
+ /** The agent drew something. Arrives the instant it is called, mid-turn. */
1116
+ | {
1117
+ event: "ui";
1118
+ data: UiRender;
1119
+ }
963
1120
  /** The agent has handed a page to the user — open a viewer, poll `browserFrame`
964
1121
  * and relay clicks with `browserInput`. It then asks a question and waits. */
965
1122
  | {
@@ -991,6 +1148,8 @@ export type StreamEvent = {
991
1148
  requires_action: boolean;
992
1149
  tool_calls: PendingToolCall[];
993
1150
  questions: PendingQuestions[];
1151
+ approvals: PendingApproval[];
1152
+ ui: UiRender[];
994
1153
  todos: TodoItem[];
995
1154
  subagents: SubagentState[];
996
1155
  handoff?: Handoff | null;
@@ -1046,6 +1205,10 @@ export interface StreamHandlers {
1046
1205
  * cannot leave the panel wrong. One that is `running` after the turn ends is still
1047
1206
  * running — keep showing it, and it will report into the next turn. */
1048
1207
  onSubagents?: (subagents: SubagentState[]) => void;
1208
+ /** UI components the agent may draw, for this stream. Their `render` is called for
1209
+ * you as each arrives; this fires alongside if you also want to observe them. */
1210
+ ui?: UiComponent<any>[];
1211
+ onUi?: (render: UiRender) => void;
1049
1212
  /** A sandbox command's output as it is produced. Append it to a live pane keyed by
1050
1213
  * `command_id` — a long build should look like it is working, and a person watching
1051
1214
  * needs to be able to tell a slow command from a stuck one. */
@@ -1075,6 +1238,9 @@ export interface StreamHandlers {
1075
1238
  * picker, resolve with what the user chose (or `{ chat_instead: true }` if they
1076
1239
  * would rather keep talking), and `done` resolves only once the agent finishes. */
1077
1240
  onQuestion?: QuestionHandler;
1241
+ /** Decide the agent's approval requests and continue the stream. Without it the
1242
+ * stream ends with `approvals` on `done` and you resume it yourself. */
1243
+ onApproval?: ApprovalHandler;
1078
1244
  /** Called each time the client (re)connects, with the resume attempt count. */
1079
1245
  onReconnect?: (attempt: number) => void;
1080
1246
  signal?: AbortSignal;
@@ -1118,6 +1284,10 @@ export interface ChatDone {
1118
1284
  tool_calls: PendingToolCall[];
1119
1285
  /** Questions the agent paused on. Empty once `onQuestion` has answered them. */
1120
1286
  questions: PendingQuestions[];
1287
+ /** Approvals the agent is waiting on. Empty once `onApproval` has decided them. */
1288
+ approvals: PendingApproval[];
1289
+ /** Components the agent drew this turn, in call order. Already rendered by then. */
1290
+ ui: UiRender[];
1121
1291
  /** The agent's plan for this session, when the todo family ran this turn. */
1122
1292
  todos: TodoItem[];
1123
1293
  /** Every subagent of the conversation. Some may still be `running` — the turn
@@ -1208,6 +1378,7 @@ export declare class AgentFramework {
1208
1378
  private readonly opts;
1209
1379
  private readonly _fetch;
1210
1380
  private readonly toolRegistry;
1381
+ private readonly uiRegistry;
1211
1382
  /** Active session watchers, so a streamed turn can mark its own messages seen. */
1212
1383
  private readonly watchers;
1213
1384
  /** The bearer in use: `opts.token` initially, replaced on refresh. */
@@ -1253,8 +1424,20 @@ export declare class AgentFramework {
1253
1424
  /** Register a client-side tool (handler run when the agent calls it). */
1254
1425
  registerTool(tool: ClientToolDef): this;
1255
1426
  registerTools(tools: ClientToolDef[]): this;
1427
+ /** Register a UI component the agent can draw into your app.
1428
+ *
1429
+ * Unlike a tool, nothing is handed back: the agent calls it, your `render` runs, and
1430
+ * the turn carries on without waiting. */
1431
+ registerUi(component: UiComponent<any>): this;
1432
+ registerUiComponents(components: UiComponent<any>[]): this;
1256
1433
  /** Merge the client-level registry with any per-call tools (per-call wins). */
1257
1434
  private resolveTools;
1435
+ private resolveUi;
1436
+ /** Draw whatever the agent asked for, in order.
1437
+ *
1438
+ * A component that throws is logged and skipped: one broken chart must not take down
1439
+ * the turn that drew it, and there is nothing to report back to the agent anyway. */
1440
+ private renderUi;
1258
1441
  /** `bearer` overrides token resolution — used to replay a request with the token a
1259
1442
  * refresh just produced, instead of asking for one again. */
1260
1443
  private authHeaders;
@@ -1448,6 +1631,42 @@ export declare class AgentFramework {
1448
1631
  }) => Promise<SessionOut>;
1449
1632
  };
1450
1633
  };
1634
+ /** Agent Plugins — the skills this token can reach for, and the ones it may add.
1635
+ *
1636
+ * Two sources, one list: what the project published plus anything this end-user
1637
+ * uploaded. Uploading needs the `plugins:write` capability; reading does not, because
1638
+ * a project that publishes a procedure wants its agent to use it. */
1639
+ plugins: {
1640
+ list: () => Promise<PublishedPlugin[]>;
1641
+ delete: (id: string) => Promise<void>;
1642
+ /** Publish a skill. Re-uploading a name replaces it.
1643
+ *
1644
+ * Takes whatever the customer actually has:
1645
+ *
1646
+ * - a packaged Agent Plugin (`plugin.json` + `skills/`), read as-is;
1647
+ * - a zipped folder of skills, or a single `SKILL.md` — a manifest is written
1648
+ * for them, because requiring one to publish a file of instructions is a
1649
+ * packaging exercise standing in front of the feature;
1650
+ * - a folder's files, from a directory picker, each keyed by its relative path.
1651
+ *
1652
+ * An end-user's plugin is private to them and unioned on top of the project's —
1653
+ * only a project key can publish to everyone. */
1654
+ upload: (file: UploadInput, opts?: {
1655
+ filename?: string;
1656
+ signal?: AbortSignal;
1657
+ }) => Promise<PublishedPlugin>;
1658
+ /** Publish a folder of skills without zipping it.
1659
+ *
1660
+ * `files` is what a browser directory picker gives you. Each part is sent under
1661
+ * its path relative to the folder, which is all the server needs to lay the
1662
+ * skills out — so no zip library is needed on your side. */
1663
+ uploadFolder: (files: {
1664
+ path: string;
1665
+ content: UploadInput;
1666
+ }[], opts?: {
1667
+ signal?: AbortSignal;
1668
+ }) => Promise<PublishedPlugin>;
1669
+ };
1451
1670
  documents: {
1452
1671
  list: (query?: {
1453
1672
  tag?: string;
@@ -1509,26 +1728,35 @@ export declare class AgentFramework {
1509
1728
  get: (id: string) => Promise<TaskOut>;
1510
1729
  cancel: (id: string) => Promise<TaskOut>;
1511
1730
  };
1512
- sources: {
1513
- list: () => Promise<SourceOut[]>;
1514
- get: (id: string) => Promise<SourceOut>;
1515
- types: () => Promise<{
1516
- types: string[];
1517
- }>;
1518
- /** Rotate credentials, retag, change the refresh cadence or the ACL. A new
1519
- * `config` is tested before it replaces the old one, so a bad DSN fails here
1520
- * (400) instead of at the next scheduled sync. Changing the interval
1521
- * reschedules; 0 turns auto-refresh off. */
1522
- patch: (id: string, body: AclFields & {
1523
- name?: string;
1524
- config?: Record<string, unknown>;
1525
- sync_interval_seconds?: number;
1526
- tags?: string[];
1527
- }) => Promise<SourceOut>;
1528
- /** Trigger a sync now; pass `fullRefresh` to re-pull everything. */
1529
- sync: (id: string, fullRefresh?: boolean) => Promise<SourceOut>;
1530
- remove: (id: string) => Promise<void>;
1731
+ /**
1732
+ * What the agent wrote down — remembered facts and wiki pages.
1733
+ *
1734
+ * Retrieval reads these back on every turn, so when an answer looks wrong a
1735
+ * remembered fact is often the reason. The ACL that governs retrieval governs this
1736
+ * too: a caller sees exactly what its token could have retrieved.
1737
+ */
1738
+ memory: {
1739
+ /** `kind` is "memory" (facts, per end-user) or "wiki" (pages, per user or shared). */
1740
+ list: (opts?: {
1741
+ kind?: "memory" | "wiki";
1742
+ limit?: number;
1743
+ }) => Promise<KnowledgeItem[]>;
1744
+ /** Forget one. The agent can write it again; this removes what is there now. */
1745
+ delete: (itemId: string) => Promise<void>;
1531
1746
  };
1747
+ /**
1748
+ * What this token can actually do.
1749
+ *
1750
+ * The three gates on a turn are the platform's kill switch, the token's capability
1751
+ * and the per-request `enable_*` flag, and until this existed a client could read
1752
+ * none of them. `flags[].effective` is the useful one: false means setting that flag
1753
+ * changes nothing on this token — which is otherwise indistinguishable from the agent
1754
+ * simply choosing not to use the tool.
1755
+ *
1756
+ * Cheap and safe to call on load: a token asking what it holds is reading its own
1757
+ * claims back, so it needs no capability of its own.
1758
+ */
1759
+ capabilities: () => Promise<TokenCapabilities>;
1532
1760
  audit: {
1533
1761
  /** Read the tenant's audit trail (admin). Filter by action/subject. */
1534
1762
  list: (opts?: {
@@ -1588,16 +1816,6 @@ export declare class AgentFramework {
1588
1816
  /** Pull a file out of the sandbox as bytes. */
1589
1817
  download: (id: string, path: string) => Promise<Blob>;
1590
1818
  };
1591
- /**
1592
- * Connect an external data source and keep it live in the agent's knowledge.
1593
- * Validates the connector, runs an initial sync, and (when an interval is set)
1594
- * schedules recurring auto-refresh so the data never goes stale.
1595
- *
1596
- * await ai.sync({ name: "orders", connector_type: "postgres",
1597
- * config: { dsn, query: "select id, status, total from orders",
1598
- * cursor_column: "updated_at" }, sync_interval_seconds: 900 });
1599
- */
1600
- sync: (config: SyncConfig) => Promise<SourceOut>;
1601
1819
  private runWithTools;
1602
1820
  /** Wrap startStream so a `done` carrying `requires_action` auto-executes the
1603
1821
  * client tools and continues the stream (same session) until the agent ends. */
@@ -1608,6 +1826,162 @@ export declare class AgentFramework {
1608
1826
  private multipartUpload;
1609
1827
  private rangedDownload;
1610
1828
  }
1829
+ /** Where projects are administered and end-user tokens are minted. A different host
1830
+ * from {@link DEFAULT_BASE_URL}, and a different credential — the two are not
1831
+ * interchangeable, which is the whole reason there are two clients. */
1832
+ export declare const DEFAULT_CONTROL_PLANE_URL = "https://oberik.com";
1833
+ export interface ProjectClientOptions {
1834
+ /** From the project's URL in the dashboard. */
1835
+ projectId: string;
1836
+ /** `pk_…`. Server-side only — see the constructor. */
1837
+ projectKey: string;
1838
+ /** Only for a self-hosted control plane. */
1839
+ baseUrl?: string;
1840
+ fetch?: typeof fetch;
1841
+ }
1842
+ export interface MintTokenInput {
1843
+ /** Who this token is. Owns whatever it creates, and is the default visibility
1844
+ * boundary. A hierarchical path: `acme:finance:ana`. */
1845
+ subject: string;
1846
+ /** What it may SEE — a prefix of `subject`. Omit for "only its own data". */
1847
+ scope?: string;
1848
+ /** What it may DO. Intersected with the project's ceiling: you can narrow, never
1849
+ * widen. Omit and the token carries everything the project allows. */
1850
+ capabilities?: string[];
1851
+ roles?: string[];
1852
+ groups?: string[];
1853
+ /** Restrict this token to a subset of the project's models. */
1854
+ models?: string[];
1855
+ maxEffort?: "minimal" | "low" | "medium" | "high";
1856
+ /** Ceiling on agent↔tool loops. Unset means unlimited. */
1857
+ maxToolIterations?: number;
1858
+ maxContextTokens?: number;
1859
+ /** Seconds. Keep it short and mint per session. */
1860
+ expiresIn?: number;
1861
+ }
1862
+ export interface MintedToken {
1863
+ access_token: string;
1864
+ token_type: string;
1865
+ expires_in: number;
1866
+ tenant_id: string;
1867
+ /** What was ACTUALLY granted. Compare with what you asked for to see what the
1868
+ * project's ceiling trimmed — a capability you expected and did not get is a
1869
+ * toggle in the dashboard, not a bug in your code. */
1870
+ capabilities: string[];
1871
+ scope: string | null;
1872
+ allowed_models: string[] | null;
1873
+ max_effort: string | null;
1874
+ max_context_tokens: number | null;
1875
+ }
1876
+ /**
1877
+ * The control plane, from your backend.
1878
+ *
1879
+ * The other client in this package talks to the data plane as one end-user. This one
1880
+ * holds the project key and administers the project itself: minting those tokens,
1881
+ * setting the capability ceiling, curating the corpus.
1882
+ *
1883
+ * They are separate classes on purpose. A project key can mint a token with any
1884
+ * capability the project allows — and create further keys, and delete the project — so
1885
+ * it must never travel to the same place an end-user token does. Two types make that a
1886
+ * decision someone has to make rather than a field they can accidentally set.
1887
+ */
1888
+ export declare class OberikProject {
1889
+ readonly baseUrl: string;
1890
+ private readonly projectId;
1891
+ private readonly key;
1892
+ private readonly _fetch;
1893
+ constructor(opts: ProjectClientOptions);
1894
+ private request;
1895
+ tokens: {
1896
+ /**
1897
+ * Mint a short-lived token for ONE end-user.
1898
+ *
1899
+ * const { access_token } = await oberik.tokens.mint({
1900
+ * subject: `${user.orgId}:${user.id}`,
1901
+ * scope: `${user.orgId}:${user.id}`,
1902
+ * capabilities: ["chat", "documents:read"],
1903
+ * });
1904
+ */
1905
+ mint: (input: MintTokenInput) => Promise<MintedToken>;
1906
+ /**
1907
+ * The same thing shaped as the callback {@link createClient} wants, so the two
1908
+ * halves of this package fit together without a wrapper:
1909
+ *
1910
+ * const ai = createClient({ getToken: oberik.tokens.forUser({ subject: id }) });
1911
+ *
1912
+ * Called again whenever a token expires, so the client refreshes on its own.
1913
+ */
1914
+ forUser: (input: MintTokenInput) => () => Promise<string>;
1915
+ };
1916
+ /** The capability ceiling: the maximum any token minted here may hold. */
1917
+ capabilities: {
1918
+ get: () => Promise<Record<string, unknown>>;
1919
+ /** Merges — send only what you want to change. */
1920
+ set: (caps: Record<string, unknown>) => Promise<unknown>;
1921
+ };
1922
+ /** Documents owned by the project rather than by any one end-user: the corpus you
1923
+ * curate and your users only read. */
1924
+ documents: {
1925
+ list: () => Promise<unknown[]>;
1926
+ upload: (file: Blob | File, opts?: {
1927
+ filename?: string;
1928
+ tags?: string[];
1929
+ }) => Promise<unknown>;
1930
+ delete: (documentId: string) => Promise<void>;
1931
+ };
1932
+ /** Prepended to every request for this project, above anything a caller sends. */
1933
+ systemPrompt: {
1934
+ set: (systemPrompt: string) => Promise<unknown>;
1935
+ };
1936
+ /** Browser origins allowed to call the data plane with this project's tokens. */
1937
+ origins: {
1938
+ set: (origins: string[]) => Promise<unknown>;
1939
+ };
1940
+ /** Tools the agent calls by URL. The signing secret comes back once, on create. */
1941
+ webhookTools: {
1942
+ list: () => Promise<unknown[]>;
1943
+ create: (tool: {
1944
+ name: string;
1945
+ description: string;
1946
+ url: string;
1947
+ parameters?: Record<string, unknown>;
1948
+ headers?: Record<string, string>;
1949
+ }) => Promise<{
1950
+ id: string;
1951
+ secret: string;
1952
+ }>;
1953
+ delete: (toolId: string) => Promise<void>;
1954
+ };
1955
+ /** Server-side keys. A created one is returned once and never again. */
1956
+ /**
1957
+ * Further project keys.
1958
+ *
1959
+ * `scope` is the important argument and it defaults to the narrow one. A `mint` key
1960
+ * can turn your signed-in user into an end-user token and nothing else — it cannot
1961
+ * read the corpus, raise the capability ceiling, issue more keys, or delete the
1962
+ * project. That is what almost every backend actually needs, and it is the difference
1963
+ * between a leaked key costing you some tokens and costing you the workspace.
1964
+ */
1965
+ keys: {
1966
+ list: () => Promise<ProjectKeyInfo[]>;
1967
+ create: (name: string, opts?: {
1968
+ scope?: ProjectKeyScope;
1969
+ }) => Promise<{
1970
+ id: string;
1971
+ key: string;
1972
+ scope: ProjectKeyScope;
1973
+ }>;
1974
+ revoke: (keyId: string) => Promise<void>;
1975
+ };
1976
+ /** Spend, requests, tokens and latency — including per end-user, since spend is
1977
+ * attributed to the token's subject. */
1978
+ usage: {
1979
+ summary: () => Promise<unknown>;
1980
+ observability: (windowSeconds?: number) => Promise<unknown>;
1981
+ };
1982
+ }
1983
+ /** Factory helper for the server-side client. */
1984
+ export declare function createProjectClient(opts: ProjectClientOptions): OberikProject;
1611
1985
  /** Factory helper. */
1612
1986
  export declare function createClient(opts: ClientOptions): AgentFramework;
1613
1987
  export default AgentFramework;