@truefoundry/assistant-ui-runtime 0.1.6 → 0.1.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 (30) hide show
  1. package/README.md +3 -2
  2. package/dist/{chunk-Q2SHKMLM.js → chunk-SQDOTGP2.js} +32 -10
  3. package/dist/chunk-SQDOTGP2.js.map +1 -0
  4. package/dist/index.d.ts +19 -23
  5. package/dist/index.js +256 -162
  6. package/dist/index.js.map +1 -1
  7. package/dist/plugins/truefoundry-agent-server-adapter/index.d.ts +3 -6
  8. package/dist/plugins/truefoundry-agent-server-adapter/index.js +1 -1
  9. package/dist/server/index.d.ts +2 -2
  10. package/dist/{types-BfiFf8O1.d.ts → types-DbNsU075.d.ts} +196 -9
  11. package/package.json +1 -1
  12. package/src/{private → draft}/agentSpec.ts +14 -17
  13. package/src/{private → draft}/draftSessionBridge.ts +1 -2
  14. package/src/{private → draft}/truefoundryDraftThreadListAdapter.test.ts +1 -1
  15. package/src/{private → draft}/truefoundryDraftThreadListAdapter.ts +2 -1
  16. package/src/{private → draft}/useDraftAgentSpec.ts +16 -5
  17. package/src/draftAgentConfig.test.ts +2 -1
  18. package/src/index.ts +30 -7
  19. package/src/plugins/truefoundry-agent-server-adapter/index.ts +51 -11
  20. package/src/plugins/truefoundry-agent-server-adapter/types.ts +6 -4
  21. package/src/server/index.ts +23 -0
  22. package/src/server/types.ts +256 -11
  23. package/src/truefoundryExtras.ts +4 -1
  24. package/src/truefoundryOwnedSessionsThreadListAdapter.ts +1 -1
  25. package/src/types.ts +1 -2
  26. package/src/useTrueFoundryAgentMessages.test.tsx +261 -1
  27. package/src/useTrueFoundryAgentMessages.ts +284 -176
  28. package/src/useTrueFoundryAgentRuntime.ts +31 -21
  29. package/dist/chunk-Q2SHKMLM.js.map +0 -1
  30. /package/src/{private → draft}/useDraftAgentSpec.test.tsx +0 -0
@@ -1,6 +1,6 @@
1
1
  import { AgentSessionClient } from 'truefoundry-gateway-sdk/agents';
2
2
  import { PrivateAgentSessionClient } from 'truefoundry-gateway-sdk/agents/private';
3
- import { A as AgentSpec, C as CreateSessionRequest, L as ListSessionsParams, S as Session, T as Turn, a as TurnState, b as AgentChatServer, U as UpdateSessionRequest } from '../../types-BfiFf8O1.js';
3
+ import { A as AgentSpec, C as CreateSessionRequest, L as ListSessionsParams, S as Session, T as Turn, a as TurnState, b as AgentChatServer, U as UpdateSessionRequest } from '../../types-DbNsU075.js';
4
4
  import { TruefoundryGatewayApi } from 'truefoundry-gateway-sdk';
5
5
 
6
6
  /**
@@ -29,10 +29,7 @@ type RuntimeSkillMount = NonNullable<NonNullable<AgentSpec["skills"]>[number]>;
29
29
  type RuntimeMcpServerMount = NonNullable<NonNullable<AgentSpec["mcpServers"]>[number]>;
30
30
  type TfySkillMount = RuntimeSkillMount & TruefoundryGatewayApi.SkillMount;
31
31
  type TfyMcpServerMount = RuntimeMcpServerMount & TruefoundryGatewayApi.McpServer;
32
- interface TfyAgentSpec extends AgentSpec {
33
- model: TruefoundryGatewayApi.Model;
34
- skills?: TfySkillMount[];
35
- mcpServers?: TfyMcpServerMount[];
32
+ interface TfyAgentSpec extends AgentSpec<TruefoundryGatewayApi.Model, TfySkillMount, TfyMcpServerMount> {
36
33
  config?: TruefoundryGatewayApi.RuntimeConfig;
37
34
  responseFormat?: TruefoundryGatewayApi.ResponseFormat;
38
35
  messages?: TruefoundryGatewayApi.AgentSpecUserMessage[];
@@ -155,7 +152,7 @@ type TrueFoundryChatServer<TSpec extends TfyAgentSpec = TfyAgentSpec> = AgentCha
155
152
  * Wraps TrueFoundry gateway clients into a flat `AgentChatServer`.
156
153
  * Named vs draft routing is fully internal — an in-memory session-type cache
157
154
  * (populated by createSession/listSessions) determines which gateway client
158
- * to call. No try/catch fallback, no double network calls.
155
+ * to call, falling back to a one-time probe for ids seen only in a URL.
159
156
  */
160
157
  declare function createTrueFoundryChatServer<TSpec extends TfyAgentSpec = TfyAgentSpec>(opts: CreateTrueFoundryChatServerOptions): TrueFoundryChatServer<TSpec>;
161
158
 
@@ -6,7 +6,7 @@ import {
6
6
  isTfyMcpToolInfo,
7
7
  isTfySystemToolInfo,
8
8
  isTfyToolInfo
9
- } from "../../chunk-Q2SHKMLM.js";
9
+ } from "../../chunk-SQDOTGP2.js";
10
10
  export {
11
11
  createTrueFoundryChatServer,
12
12
  getTfyMcpInitServers,
@@ -1,5 +1,5 @@
1
- import { r as TurnStreamingEvent, D as DeltaEvents, c as TurnEvent } from '../types-BfiFf8O1.js';
2
- export { t as ActionRequiredEvent, h as AgentBuilderServer, b as AgentChatServer, u as AgentInfo, v as AgentParent, w as AgentSelectorEntry, A as AgentSpec, x as ApprovalDecision, y as ChunkDeltaToolCall, z as ConnectorSelectorEntry, C as CreateSessionRequest, i as ListResult, B as ListSessionsOrder, L as ListSessionsParams, M as McpAuthRequiredEvent, E as McpInitializeEvent, F as McpServerAuthInfo, G as McpServerMount, H as Model, I as ModelMessageContentPart, J as ModelMessageDeltaEvent, j as ModelMessageEvent, K as ModelParams, N as ModelSelectorEntry, O as PageParams, P as PreviousTurnIdInput, k as SandboxCreatedEvent, Q as SearchAgentSelectorParams, S as Session, l as SessionEventItem, R as SkillMount, V as SkillSelectorEntry, d as ThreadCreatedEvent, W as ThreadDoneEvent, m as ToolApprovalRequiredEvent, n as ToolCall, X as ToolCallFunction, Y as ToolCallRef, Z as ToolInfo, _ as ToolResponseEvent, o as ToolResponseRequiredEvent, T as Turn, $ as TurnCreatedEvent, a0 as TurnDoneEvent, g as TurnInputItem, a as TurnState, a1 as TurnStateCancelled, p as TurnStateDone, a2 as TurnStateError, a3 as TurnStateRunning, q as TurnStreamData, U as UpdateSessionRequest, s as UserMessage, a4 as UserMessageContent, f as UserToolApprovalEvent, e as UserToolResponseEvent } from '../types-BfiFf8O1.js';
1
+ import { R as TurnStreamingEvent, Y as DeltaEvents, c as TurnEvent } from '../types-DbNsU075.js';
2
+ export { Z as ActionRequiredEvent, h as AgentBuilderServer, b as AgentChatServer, _ as AgentInfo, $ as AgentParent, a0 as AgentSelectorEntry, A as AgentSpec, i as AgentUIServerPort, a1 as ApprovalDecision, j as CatalogServer, a2 as ChunkDeltaToolCall, k as ConnectorAuth, l as ConnectorAuthPublic, m as ConnectorAuthType, n as ConnectorBase, o as ConnectorCatalogEntry, p as ConnectorCatalogServer, q as ConnectorConfigBase, a3 as ConnectorSelectorEntry, r as CreateConnectorRequest, s as CreateModelProviderRequest, C as CreateSessionRequest, t as CreateSkillRequest, u as ListResult, a4 as ListSessionsOrder, L as ListSessionsParams, M as McpAuthRequiredEvent, a5 as McpInitializeEvent, a6 as McpServerAuthInfo, a7 as McpServerMount, a8 as Model, v as ModelCatalogServer, w as ModelEntry, a9 as ModelMessageContentPart, aa as ModelMessageDeltaEvent, x as ModelMessageEvent, ab as ModelParams, y as ModelProviderBase, z as ModelProviderCatalogEntry, B as ModelProviderConfigBase, ac as ModelSelectorEntry, ad as PageParams, P as PreviousTurnIdInput, D as ProviderType, E as SandboxCreatedEvent, ae as SearchAgentSelectorParams, S as Session, F as SessionEventItem, G as SkillBase, H as SkillCatalogServer, af as SkillMount, ag as SkillSelectorEntry, d as ThreadCreatedEvent, ah as ThreadDoneEvent, I as ToolApprovalRequiredEvent, J as ToolBase, K as ToolCall, ai as ToolCallFunction, aj as ToolCallRef, ak as ToolInfo, al as ToolResponseEvent, N as ToolResponseRequiredEvent, T as Turn, am as TurnCreatedEvent, an as TurnDoneEvent, g as TurnInputItem, a as TurnState, ao as TurnStateCancelled, O as TurnStateDone, ap as TurnStateError, aq as TurnStateRunning, Q as TurnStreamData, V as UpdateConnectorRequest, W as UpdateModelProviderRequest, U as UpdateSessionRequest, X as UserMessage, ar as UserMessageContent, f as UserToolApprovalEvent, e as UserToolResponseEvent } from '../types-DbNsU075.js';
3
3
 
4
4
  /**
5
5
  * Local implementations of streaming delta helpers.
@@ -279,17 +279,15 @@ interface Model {
279
279
  }
280
280
  /**
281
281
  * SDK-owned agent definition — fields the FE reads/writes.
282
- * Host adds additional fields via `TSpec extends AgentSpec`.
282
+ * Host widens `model` / `skills` / `mcpServers` via type params, and adds
283
+ * extra fields via `TSpec extends AgentSpec<...>`.
283
284
  */
284
- interface AgentSpec {
285
- model: Model;
286
- skills?: SkillMount[];
287
- mcpServers?: McpServerMount[];
285
+ interface AgentSpec<TModel extends Model = Model, TSkill extends SkillMount = SkillMount, TMcp extends McpServerMount = McpServerMount> {
286
+ model: TModel;
287
+ skills?: TSkill[];
288
+ mcpServers?: TMcp[];
288
289
  instructions?: string;
289
- messages?: unknown[];
290
290
  variables?: Record<string, string>;
291
- responseFormat?: unknown;
292
- config?: unknown;
293
291
  }
294
292
  interface Session<TSpec extends AgentSpec = AgentSpec> {
295
293
  id: string;
@@ -464,5 +462,194 @@ interface AgentBuilderServer<TSpec extends AgentSpec = AgentSpec, TModel extends
464
462
  agentName: string;
465
463
  }): Promise<void>;
466
464
  }
465
+ /**
466
+ * Provider type id. Reserved literal: `"custom"` for user-defined providers;
467
+ * any other string is a builtin (e.g. `"openai"`, `"anthropic"`).
468
+ *
469
+ * Note: `string | "custom"` is useless in TypeScript (`"custom"` ⊆ `string`),
470
+ * so this stays `string` and `"custom"` is a documented convention.
471
+ */
472
+ type ProviderType = string;
473
+ /**
474
+ * Model row — form "Model ID" + "Display name".
475
+ * Host extends for properties, etc.
476
+ */
477
+ interface ModelEntry {
478
+ id: string;
479
+ name: string;
480
+ }
481
+ /**
482
+ * Write config for create/update (custom form + catalog "Save key").
483
+ * Host extends. `baseUrl` present iff `type === "custom"`.
484
+ */
485
+ interface ModelProviderConfigBase<TModel extends ModelEntry = ModelEntry> {
486
+ type: ProviderType;
487
+ name: string;
488
+ /** Present iff `type === "custom"`. */
489
+ baseUrl?: string;
490
+ apiKey: string;
491
+ models: TModel[];
492
+ }
493
+ /**
494
+ * Configured provider card (list/read). No raw `apiKey`.
495
+ * Host extends for apiKeySet, timestamps, etc.
496
+ */
497
+ interface ModelProviderBase<TModel extends ModelEntry = ModelEntry> {
498
+ id: string;
499
+ type: ProviderType;
500
+ name: string;
501
+ /** Present iff `type === "custom"`. */
502
+ baseUrl?: string;
503
+ models: TModel[];
504
+ }
505
+ /**
506
+ * Discovery-only catalog provider (AVAILABLE list).
507
+ * `type` must not be `"custom"` — custom providers use the custom form.
508
+ * Host extends for richer model rows.
509
+ */
510
+ interface ModelProviderCatalogEntry<TModel extends ModelEntry = ModelEntry> {
511
+ type: ProviderType;
512
+ name: string;
513
+ models: TModel[];
514
+ }
515
+ /** Create — no `id`; server assigns it. Catalog path = entry + apiKey. */
516
+ type CreateModelProviderRequest<TModel extends ModelEntry = ModelEntry> = ModelProviderConfigBase<TModel>;
517
+ /** Update — `id` required. */
518
+ type UpdateModelProviderRequest<TModel extends ModelEntry = ModelEntry> = ModelProviderConfigBase<TModel> & {
519
+ id: string;
520
+ };
521
+ interface ModelCatalogServer<TModel extends ModelEntry = ModelEntry, TProvider extends ModelProviderBase<TModel> = ModelProviderBase<TModel>, TCatalogProvider extends ModelProviderCatalogEntry<TModel> = ModelProviderCatalogEntry<TModel>, TCreate extends CreateModelProviderRequest<TModel> = CreateModelProviderRequest<TModel>, TUpdate extends UpdateModelProviderRequest<TModel> = UpdateModelProviderRequest<TModel>> {
522
+ getModelProviderCatalog(): Promise<TCatalogProvider[]>;
523
+ listModelProviders(): Promise<TProvider[]>;
524
+ createModelProvider(req: TCreate): Promise<TProvider>;
525
+ /** Full replace update keyed by provider `id`. */
526
+ updateModelProvider(req: TUpdate): Promise<TProvider>;
527
+ deleteModelProvider?(req: {
528
+ id: string;
529
+ }): Promise<void>;
530
+ }
531
+ /** Tool row on a connector detail. Host extends for schemas, etc. */
532
+ interface ToolBase {
533
+ id: string;
534
+ name: string;
535
+ }
536
+ /**
537
+ * Auth type id. Reserved literals: `"None"`, `"OAuth"`, `"API Key"`.
538
+ * Stays `string` so hosts can widen (same pattern as `ProviderType`).
539
+ */
540
+ type ConnectorAuthType = string;
541
+ /**
542
+ * Write-time connector auth. Host extends / narrows via `TType`.
543
+ * For `"API Key"`, pass `apiKey` (and optional `headerName`).
544
+ */
545
+ interface ConnectorAuth<TType extends ConnectorAuthType = ConnectorAuthType> {
546
+ type: TType;
547
+ apiKey?: string;
548
+ headerName?: string;
549
+ }
550
+ /**
551
+ * Catalog / list auth — no secrets. Host extends / narrows via `TType`.
552
+ */
553
+ interface ConnectorAuthPublic<TType extends ConnectorAuthType = ConnectorAuthType> {
554
+ type: TType;
555
+ headerName?: string;
556
+ }
557
+ /**
558
+ * MCP / connector create-edit config. Host extends for extra fields, etc.
559
+ */
560
+ interface ConnectorConfigBase<TAuth extends ConnectorAuth = ConnectorAuth> {
561
+ name: string;
562
+ url: string;
563
+ auth: TAuth;
564
+ }
565
+ /**
566
+ * Connected connector row (settings/connectors). No raw `apiKey`.
567
+ * Host extends.
568
+ */
569
+ interface ConnectorBase<TTool extends ToolBase = ToolBase, TAuth extends ConnectorAuthPublic = ConnectorAuthPublic> {
570
+ id: string;
571
+ name: string;
572
+ description: string;
573
+ url: string;
574
+ auth: TAuth;
575
+ authenticated: boolean;
576
+ tools: TTool[];
577
+ }
578
+ /** Discovery catalog entry for "+ Add MCP server". Host extends. */
579
+ interface ConnectorCatalogEntry<TAuth extends ConnectorAuthPublic = ConnectorAuthPublic> {
580
+ id: string;
581
+ name: string;
582
+ description?: string;
583
+ url: string;
584
+ auth: TAuth;
585
+ }
586
+ /** Create connector — no `id`; server assigns it. Host extends. */
587
+ type CreateConnectorRequest<TAuth extends ConnectorAuth = ConnectorAuth> = ConnectorConfigBase<TAuth>;
588
+ /** Update connector — `id` required. Host extends. */
589
+ type UpdateConnectorRequest<TAuth extends ConnectorAuth = ConnectorAuth> = ConnectorConfigBase<TAuth> & {
590
+ id: string;
591
+ };
592
+ interface ConnectorCatalogServer<TTool extends ToolBase = ToolBase, TAuthWrite extends ConnectorAuth = ConnectorAuth, TAuthPublic extends ConnectorAuthPublic = ConnectorAuthPublic, TConnector extends ConnectorBase<TTool, TAuthPublic> = ConnectorBase<TTool, TAuthPublic>, TCatalogEntry extends ConnectorCatalogEntry<TAuthPublic> = ConnectorCatalogEntry<TAuthPublic>, TCreate extends CreateConnectorRequest<TAuthWrite> = CreateConnectorRequest<TAuthWrite>, TUpdate extends UpdateConnectorRequest<TAuthWrite> = UpdateConnectorRequest<TAuthWrite>> {
593
+ getConnectorCatalog(): Promise<TCatalogEntry[]>;
594
+ listConnectors(req?: {
595
+ query?: string;
596
+ }): Promise<TConnector[]>;
597
+ createConnector(req: TCreate): Promise<TConnector>;
598
+ /** Full replace update keyed by connector `id`. */
599
+ updateConnector(req: TUpdate): Promise<TConnector>;
600
+ /** Start connector auth (e.g. OAuth). Host may widen return with `authUrl`. */
601
+ authenticateConnector(req: {
602
+ id: string;
603
+ }): Promise<TConnector>;
604
+ /** Clear connector auth. */
605
+ disconnectConnector(req: {
606
+ id: string;
607
+ }): Promise<TConnector>;
608
+ deleteConnector?(req: {
609
+ id: string;
610
+ }): Promise<void>;
611
+ }
612
+ /** Skill row shown in settings/skills (list + delete). Host extends for fqn, etc. */
613
+ interface SkillBase {
614
+ id: string;
615
+ name: string;
616
+ description: string;
617
+ }
618
+ /** Create-skill request. Host extends for branch, auth, etc. */
619
+ interface CreateSkillRequest {
620
+ repo: string;
621
+ directory: string;
622
+ }
623
+ interface SkillCatalogServer<TSkill extends SkillBase = SkillBase, TCreate extends CreateSkillRequest = CreateSkillRequest> {
624
+ listSkills(req?: {
625
+ query?: string;
626
+ }): Promise<TSkill[]>;
627
+ createSkill(req: TCreate): Promise<TSkill>;
628
+ deleteSkill?(req: {
629
+ id: string;
630
+ }): Promise<void>;
631
+ }
632
+ /**
633
+ * Settings management aggregate — modelCatalog + connectorCatalog + optional skillCatalog.
634
+ * Hosts may pass the whole object to an app shell, or a focused sub-port to a page.
635
+ */
636
+ interface CatalogServer<TModelCatalog extends ModelCatalogServer = ModelCatalogServer, TConnectorCatalog extends ConnectorCatalogServer = ConnectorCatalogServer, TSkillCatalog extends SkillCatalogServer = SkillCatalogServer> {
637
+ modelCatalog: TModelCatalog;
638
+ connectorCatalog: TConnectorCatalog;
639
+ /** Optional — omit when the host has no skills settings surface. */
640
+ skillCatalog?: TSkillCatalog;
641
+ }
642
+ /**
643
+ * Composed host port: chat + builder + optional settings catalog.
644
+ * Agent-ui's `AgentUIServer` mirrors this shape; named differently here to
645
+ * avoid colliding with that package's local type name.
646
+ *
647
+ * `catalog` is optional — if the host passes it, settings UI can call
648
+ * `useCatalogServer()` / show modelCatalog, connectorCatalog, and skillCatalog;
649
+ * if omitted, those surfaces stay hidden.
650
+ */
651
+ type AgentUIServerPort<TChat extends AgentChatServer = AgentChatServer, TBuilder extends AgentBuilderServer = AgentBuilderServer, TCatalog extends CatalogServer = CatalogServer> = TChat & TBuilder & {
652
+ catalog?: TCatalog;
653
+ };
467
654
 
468
- export type { TurnCreatedEvent as $, AgentSpec as A, ListSessionsOrder as B, CreateSessionRequest as C, DeltaEvents as D, McpInitializeEvent as E, McpServerAuthInfo as F, McpServerMount as G, Model as H, ModelMessageContentPart as I, ModelMessageDeltaEvent as J, ModelParams as K, ListSessionsParams as L, McpAuthRequiredEvent as M, ModelSelectorEntry as N, PageParams as O, PreviousTurnIdInput as P, SearchAgentSelectorParams as Q, SkillMount as R, Session as S, Turn as T, UpdateSessionRequest as U, SkillSelectorEntry as V, ThreadDoneEvent as W, ToolCallFunction as X, ToolCallRef as Y, ToolInfo as Z, ToolResponseEvent as _, TurnState as a, TurnDoneEvent as a0, TurnStateCancelled as a1, TurnStateError as a2, TurnStateRunning as a3, UserMessageContent as a4, AgentChatServer as b, TurnEvent as c, ThreadCreatedEvent as d, UserToolResponseEvent as e, UserToolApprovalEvent as f, TurnInputItem as g, AgentBuilderServer as h, ListResult as i, ModelMessageEvent as j, SandboxCreatedEvent as k, SessionEventItem as l, ToolApprovalRequiredEvent as m, ToolCall as n, ToolResponseRequiredEvent as o, TurnStateDone as p, TurnStreamData as q, TurnStreamingEvent as r, UserMessage as s, ActionRequiredEvent as t, AgentInfo as u, AgentParent as v, AgentSelectorEntry as w, ApprovalDecision as x, ChunkDeltaToolCall as y, ConnectorSelectorEntry as z };
655
+ export type { AgentParent as $, AgentSpec as A, ModelProviderConfigBase as B, CreateSessionRequest as C, ProviderType as D, SandboxCreatedEvent as E, SessionEventItem as F, SkillBase as G, SkillCatalogServer as H, ToolApprovalRequiredEvent as I, ToolBase as J, ToolCall as K, ListSessionsParams as L, McpAuthRequiredEvent as M, ToolResponseRequiredEvent as N, TurnStateDone as O, PreviousTurnIdInput as P, TurnStreamData as Q, TurnStreamingEvent as R, Session as S, Turn as T, UpdateSessionRequest as U, UpdateConnectorRequest as V, UpdateModelProviderRequest as W, UserMessage as X, DeltaEvents as Y, ActionRequiredEvent as Z, AgentInfo as _, TurnState as a, AgentSelectorEntry as a0, ApprovalDecision as a1, ChunkDeltaToolCall as a2, ConnectorSelectorEntry as a3, ListSessionsOrder as a4, McpInitializeEvent as a5, McpServerAuthInfo as a6, McpServerMount as a7, Model as a8, ModelMessageContentPart as a9, ModelMessageDeltaEvent as aa, ModelParams as ab, ModelSelectorEntry as ac, PageParams as ad, SearchAgentSelectorParams as ae, SkillMount as af, SkillSelectorEntry as ag, ThreadDoneEvent as ah, ToolCallFunction as ai, ToolCallRef as aj, ToolInfo as ak, ToolResponseEvent as al, TurnCreatedEvent as am, TurnDoneEvent as an, TurnStateCancelled as ao, TurnStateError as ap, TurnStateRunning as aq, UserMessageContent as ar, AgentChatServer as b, TurnEvent as c, ThreadCreatedEvent as d, UserToolResponseEvent as e, UserToolApprovalEvent as f, TurnInputItem as g, AgentBuilderServer as h, AgentUIServerPort as i, CatalogServer as j, ConnectorAuth as k, ConnectorAuthPublic as l, ConnectorAuthType as m, ConnectorBase as n, ConnectorCatalogEntry as o, ConnectorCatalogServer as p, ConnectorConfigBase as q, CreateConnectorRequest as r, CreateModelProviderRequest as s, CreateSkillRequest as t, ListResult as u, ModelCatalogServer as v, ModelEntry as w, ModelMessageEvent as x, ModelProviderBase as y, ModelProviderCatalogEntry as z };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@truefoundry/assistant-ui-runtime",
3
- "version": "0.1.6",
3
+ "version": "0.1.7",
4
4
  "description": "TrueFoundry Gateway agent runtime adapter for assistant-ui",
5
5
  "license": "Apache-2.0",
6
6
  "repository": {
@@ -1,33 +1,30 @@
1
1
  import type { AgentSpec } from "../server/types.js";
2
2
 
3
- export type { AgentSpec } from "../server/types.js";
4
-
5
3
  /** @deprecated Use Session with isMutable: true instead. Kept for title helper. */
6
4
  export type DraftSession = {
7
5
  title?: string | null;
8
6
  agentSpec: AgentSpec;
9
7
  };
10
8
 
11
- export type AgentSpecUpdate = {
12
- instructions?: string;
13
- model?: Partial<AgentSpec["model"]> & {
14
- params?: Partial<NonNullable<AgentSpec["model"]["params"]>>;
15
- };
16
- mcpServers?: AgentSpec["mcpServers"];
17
- skills?: AgentSpec["skills"];
18
- messages?: AgentSpec["messages"];
19
- variables?: AgentSpec["variables"];
20
- responseFormat?: AgentSpec["responseFormat"];
21
- config?: AgentSpec["config"];
9
+ /** Partial update host fields flow through when `TSpec` is widened. */
10
+ export type AgentSpecUpdate<TSpec extends AgentSpec = AgentSpec> = {
11
+ [K in keyof TSpec]?: K extends "model"
12
+ ? Omit<Partial<TSpec["model"]>, "params"> & {
13
+ params?: Partial<NonNullable<TSpec["model"]["params"]>>;
14
+ }
15
+ : TSpec[K];
22
16
  };
23
17
 
24
- export function mergeAgentSpec(base: AgentSpec, update: AgentSpecUpdate): AgentSpec {
18
+ export function mergeAgentSpec<TSpec extends AgentSpec>(
19
+ base: TSpec,
20
+ update: AgentSpecUpdate<TSpec>,
21
+ ): TSpec {
25
22
  const { model: modelUpdate, ...rest } = update;
26
23
 
27
- const next: AgentSpec = {
24
+ const next = {
28
25
  ...base,
29
26
  ...rest,
30
- };
27
+ } as TSpec;
31
28
 
32
29
  if (modelUpdate != null) {
33
30
  next.model = {
@@ -38,7 +35,7 @@ export function mergeAgentSpec(base: AgentSpec, update: AgentSpecUpdate): AgentS
38
35
  modelUpdate.params != null
39
36
  ? { ...base.model.params, ...modelUpdate.params }
40
37
  : base.model.params,
41
- };
38
+ } as TSpec["model"];
42
39
  }
43
40
 
44
41
  return next;
@@ -1,5 +1,4 @@
1
- import type { AgentChatServer } from "../server/types.js";
2
- import type { AgentSpec } from "./agentSpec.js";
1
+ import type { AgentChatServer, AgentSpec } from "../server/types.js";
3
2
 
4
3
  export const DRAFT_SESSION_LAST_UPDATED_AT_HEADER = "x-tfy-session-last-updated-at";
5
4
 
@@ -3,7 +3,7 @@ import { describe, expect, it, vi } from "vitest";
3
3
  import type { AgentChatServer, Session } from "../server/index.js";
4
4
 
5
5
  import { createTrueFoundryDraftThreadListAdapter } from "./truefoundryDraftThreadListAdapter.js";
6
- import type { AgentSpec } from "./agentSpec.js";
6
+ import type { AgentSpec } from "../server/types.js";
7
7
 
8
8
  const defaultAgentSpec: AgentSpec = {
9
9
  model: { name: "anthropic/claude-sonnet-4-6" },
@@ -1,7 +1,8 @@
1
1
  import type { RemoteThreadListAdapter } from "@assistant-ui/core";
2
2
 
3
3
  import type { AgentChatServer } from "../server/types.js";
4
- import { draftSessionTitle, type AgentSpec } from "./agentSpec.js";
4
+ import type { AgentSpec } from "../server/types.js";
5
+ import { draftSessionTitle } from "./agentSpec.js";
5
6
  import { sessionListStartTimestamp } from "../sessionListStartTimestamp.js";
6
7
 
7
8
  const THREAD_LIST_PAGE_SIZE = 20;
@@ -2,11 +2,8 @@
2
2
 
3
3
  import { useCallback, useEffect, useMemo, useRef, useState } from "react";
4
4
 
5
- import {
6
- mergeAgentSpec,
7
- type AgentSpec,
8
- type AgentSpecUpdate,
9
- } from "./agentSpec.js";
5
+ import type { AgentSpec } from "../server/types.js";
6
+ import { mergeAgentSpec, type AgentSpecUpdate } from "./agentSpec.js";
10
7
  import type { DraftSessionBridge } from "./draftSessionBridge.js";
11
8
 
12
9
  const SPEC_SYNC_DEBOUNCE_MS = 400;
@@ -22,6 +19,7 @@ export type UseDraftAgentSpecOptions = {
22
19
  export type UseDraftAgentSpecResult = {
23
20
  agentSpec: AgentSpec | null;
24
21
  draftSessionId: string | undefined;
22
+ isSpecLoading: boolean;
25
23
  isSpecSyncing: boolean;
26
24
  specError: unknown | null;
27
25
  updateAgentSpec: (update: AgentSpecUpdate) => void;
@@ -37,6 +35,7 @@ export function useDraftAgentSpec({
37
35
  }: UseDraftAgentSpecOptions): UseDraftAgentSpecResult {
38
36
  const enabled = draftBridge != null;
39
37
  const [agentSpec, setAgentSpec] = useState<AgentSpec>(defaultAgentSpec);
38
+ const [isSpecLoading, setIsSpecLoading] = useState(false);
40
39
  const [isSpecSyncing, setIsSpecSyncing] = useState(false);
41
40
  const [specError, setSpecError] = useState<unknown | null>(null);
42
41
 
@@ -86,6 +85,7 @@ export function useDraftAgentSpec({
86
85
  setAgentSpec(defaultAgentSpec);
87
86
  localDirtyRef.current = false;
88
87
  setSpecError(null);
88
+ setIsSpecLoading(false);
89
89
  return;
90
90
  }
91
91
 
@@ -94,6 +94,7 @@ export function useDraftAgentSpec({
94
94
  }
95
95
 
96
96
  let cancelled = false;
97
+ setIsSpecLoading(true);
97
98
  void (async () => {
98
99
  try {
99
100
  const loaded = await draftBridge.getDraftAgentSpec(draftSessionId);
@@ -106,21 +107,29 @@ export function useDraftAgentSpec({
106
107
  scheduleSpecSyncRef.current?.(draftSessionId, agentSpecRef.current);
107
108
  localDirtyRef.current = false;
108
109
  setSpecError(null);
110
+ setIsSpecLoading(false);
109
111
  return;
110
112
  }
111
113
 
112
114
  setAgentSpec(loaded);
113
115
  setSpecError(null);
116
+ setIsSpecLoading(false);
114
117
  } catch (error) {
115
118
  if (!cancelled) {
116
119
  onError?.(error);
117
120
  setSpecError(error);
121
+ setIsSpecLoading(false);
118
122
  }
119
123
  }
120
124
  })();
121
125
 
122
126
  return () => {
123
127
  cancelled = true;
128
+ // The cancelled load can no longer clear the flag itself. Releasing it
129
+ // here keeps it from sticking when the next run early-returns on an
130
+ // already-loaded draft; a run that starts a fresh load re-raises it in
131
+ // the same commit.
132
+ setIsSpecLoading(false);
124
133
  };
125
134
  }, [defaultAgentSpec, draftBridge, draftSessionId, enabled, onError]);
126
135
 
@@ -232,6 +241,7 @@ export function useDraftAgentSpec({
232
241
  () => ({
233
242
  agentSpec: enabled ? agentSpec : null,
234
243
  draftSessionId: enabled ? draftSessionId : undefined,
244
+ isSpecLoading: enabled ? isSpecLoading : false,
235
245
  isSpecSyncing: enabled ? isSpecSyncing : false,
236
246
  specError: enabled ? specError : null,
237
247
  updateAgentSpec,
@@ -241,6 +251,7 @@ export function useDraftAgentSpec({
241
251
  agentSpec,
242
252
  draftSessionId,
243
253
  enabled,
254
+ isSpecLoading,
244
255
  isSpecSyncing,
245
256
  specError,
246
257
  takeTurnHeaderTimestamp,
@@ -1,6 +1,7 @@
1
1
  import { describe, expect, it } from "vitest";
2
2
 
3
- import { mergeAgentSpec, type AgentSpec } from "./private/agentSpec.js";
3
+ import { mergeAgentSpec } from "./draft/agentSpec.js";
4
+ import type { AgentSpec } from "./server/types.js";
4
5
  import {
5
6
  resolveTrueFoundryAgentConfig,
6
7
  resolveTrueFoundryAgentRuntimeOptions,
package/src/index.ts CHANGED
@@ -13,17 +13,17 @@ export {
13
13
  export type { ConvertTurnsResult, UserMessageContent } from "./convertTurnMessages.js";
14
14
  export { ROOT_THREAD_ID } from "./constants.js";
15
15
  export type {
16
- UseTrueFoundryAgentRuntimeOptions,
17
16
  NamedAgentConfig,
18
17
  DraftAgentConfig,
19
18
  TrueFoundryAgentConfig,
19
+ UseTrueFoundryAgentRuntimeOptions,
20
20
  } from "./types.js";
21
- export type { AgentSpec, AgentSpecUpdate, DraftSession } from "./private/agentSpec.js";
22
- export { mergeAgentSpec, draftSessionTitle } from "./private/agentSpec.js";
23
- export { createTrueFoundryDraftThreadListAdapter } from "./private/truefoundryDraftThreadListAdapter.js";
21
+ export type { AgentSpecUpdate, DraftSession } from "./draft/agentSpec.js";
22
+ export { mergeAgentSpec, draftSessionTitle } from "./draft/agentSpec.js";
23
+ export { createTrueFoundryDraftThreadListAdapter } from "./draft/truefoundryDraftThreadListAdapter.js";
24
24
  export { createTrueFoundryOwnedSessionsThreadListAdapter } from "./truefoundryOwnedSessionsThreadListAdapter.js";
25
- export { createDraftSessionBridge } from "./private/draftSessionBridge.js";
26
- export type { DraftSessionBridge } from "./private/draftSessionBridge.js";
25
+ export { createDraftSessionBridge } from "./draft/draftSessionBridge.js";
26
+ export type { DraftSessionBridge } from "./draft/draftSessionBridge.js";
27
27
  export {
28
28
  useTrueFoundryAgentSpec,
29
29
  useTrueFoundryUpdateAgentSpec,
@@ -82,7 +82,7 @@ export type {
82
82
  TurnState,
83
83
  TurnStateDone,
84
84
  TurnInputItem,
85
- AgentSpec as ServerAgentSpec,
85
+ AgentSpec,
86
86
  ListResult,
87
87
  CreateSessionRequest,
88
88
  UpdateSessionRequest,
@@ -91,6 +91,29 @@ export type {
91
91
  UserToolApprovalEvent,
92
92
  UserToolResponseEvent,
93
93
  PreviousTurnIdInput,
94
+ ProviderType,
95
+ ModelEntry,
96
+ ModelProviderConfigBase,
97
+ ModelProviderBase,
98
+ ModelProviderCatalogEntry,
99
+ CreateModelProviderRequest,
100
+ UpdateModelProviderRequest,
101
+ ModelCatalogServer,
102
+ ToolBase,
103
+ ConnectorAuthType,
104
+ ConnectorAuth,
105
+ ConnectorAuthPublic,
106
+ ConnectorConfigBase,
107
+ ConnectorBase,
108
+ ConnectorCatalogEntry,
109
+ CreateConnectorRequest,
110
+ UpdateConnectorRequest,
111
+ ConnectorCatalogServer,
112
+ SkillBase,
113
+ CreateSkillRequest,
114
+ SkillCatalogServer,
115
+ CatalogServer,
116
+ AgentUIServerPort,
94
117
  } from "./server/index.js";
95
118
  export type {
96
119
  SandboxCreatedEvent,
@@ -94,6 +94,15 @@ export type TrueFoundryChatServer<TSpec extends TfyAgentSpec = TfyAgentSpec> =
94
94
  };
95
95
  };
96
96
 
97
+ /** Gateway SDK errors carry the HTTP status as `statusCode`. */
98
+ function isNotFound(error: unknown): boolean {
99
+ return (
100
+ typeof error === "object" &&
101
+ error !== null &&
102
+ (error as { statusCode?: unknown }).statusCode === 404
103
+ );
104
+ }
105
+
97
106
  function isDraft(session: GwSession): session is AgentDraftSession {
98
107
  return (session as AgentDraftSession).type === "session/draft";
99
108
  }
@@ -153,7 +162,7 @@ async function toListResult<TIn, TOut>(
153
162
  * Wraps TrueFoundry gateway clients into a flat `AgentChatServer`.
154
163
  * Named vs draft routing is fully internal — an in-memory session-type cache
155
164
  * (populated by createSession/listSessions) determines which gateway client
156
- * to call. No try/catch fallback, no double network calls.
165
+ * to call, falling back to a one-time probe for ids seen only in a URL.
157
166
  */
158
167
  export function createTrueFoundryChatServer<
159
168
  TSpec extends TfyAgentSpec = TfyAgentSpec,
@@ -166,6 +175,7 @@ export function createTrueFoundryChatServer<
166
175
  opts.privateClient ?? new PrivateAgentSessionClient(gatewayOpts);
167
176
 
168
177
  const sessionTypeCache = new Map<string, boolean>();
178
+ const sessionTypeProbes = new Map<string, Promise<boolean>>();
169
179
 
170
180
  function cacheSessionType(session: {
171
181
  id: string;
@@ -174,18 +184,48 @@ export function createTrueFoundryChatServer<
174
184
  sessionTypeCache.set(session.id, session.isMutable);
175
185
  }
176
186
 
177
- function getSessionObj(sessionId: string): Promise<GwSession> {
178
- const isMutable = sessionTypeCache.get(sessionId);
179
- if (isMutable === true) {
180
- return privateClient.getDraftSession({ draftSessionId: sessionId });
187
+ /**
188
+ * Resolving a session id straight from a URL (page reload / shared link)
189
+ * reaches the gateway before createSession or listSessions has cached the
190
+ * type, so discover it by probing the draft endpoint and falling back to
191
+ * the conversation endpoint. Concurrent callers share one probe.
192
+ */
193
+ async function probeSessionType(sessionId: string): Promise<boolean> {
194
+ const inflight = sessionTypeProbes.get(sessionId);
195
+ if (inflight != null) {
196
+ return inflight;
181
197
  }
182
- if (isMutable === false) {
183
- return client.getSession({ sessionId });
198
+ const probe = (async () => {
199
+ try {
200
+ await privateClient.getDraftSession({ draftSessionId: sessionId });
201
+ return true;
202
+ } catch (error) {
203
+ // Only a miss proves the id isn't a draft. Auth or transient
204
+ // failures would otherwise be retried as a conversation session
205
+ // and mislabel a real draft as named.
206
+ if (!isNotFound(error)) {
207
+ throw error;
208
+ }
209
+ await client.getSession({ sessionId });
210
+ return false;
211
+ }
212
+ })();
213
+ sessionTypeProbes.set(sessionId, probe);
214
+ try {
215
+ const isMutable = await probe;
216
+ sessionTypeCache.set(sessionId, isMutable);
217
+ return isMutable;
218
+ } finally {
219
+ sessionTypeProbes.delete(sessionId);
184
220
  }
185
- throw new Error(
186
- `Cannot resolve session "${sessionId}": session type not cached. ` +
187
- `Ensure createSession or listSessions was called first.`,
188
- );
221
+ }
222
+
223
+ async function getSessionObj(sessionId: string): Promise<GwSession> {
224
+ const isMutable =
225
+ sessionTypeCache.get(sessionId) ?? (await probeSessionType(sessionId));
226
+ return isMutable
227
+ ? privateClient.getDraftSession({ draftSessionId: sessionId })
228
+ : client.getSession({ sessionId });
189
229
  }
190
230
 
191
231
  const server: TrueFoundryChatServer<TSpec> = {
@@ -63,10 +63,12 @@ export type TfyMcpServerMount = RuntimeMcpServerMount & TruefoundryGatewayApi.Mc
63
63
  // AgentSpec — the concrete TrueFoundry agent definition
64
64
  // ---------------------------------------------------------------------------
65
65
 
66
- export interface TfyAgentSpec extends AgentSpec {
67
- model: TruefoundryGatewayApi.Model;
68
- skills?: TfySkillMount[];
69
- mcpServers?: TfyMcpServerMount[];
66
+ export interface TfyAgentSpec
67
+ extends AgentSpec<
68
+ TruefoundryGatewayApi.Model,
69
+ TfySkillMount,
70
+ TfyMcpServerMount
71
+ > {
70
72
  config?: TruefoundryGatewayApi.RuntimeConfig;
71
73
  responseFormat?: TruefoundryGatewayApi.ResponseFormat;
72
74
  messages?: TruefoundryGatewayApi.AgentSpecUserMessage[];