@truefoundry/assistant-ui-runtime 0.1.6 → 0.1.8

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 (46) hide show
  1. package/CHANGELOG.md +23 -0
  2. package/README.md +22 -17
  3. package/dist/chunk-CXBZ6WLZ.js +636 -0
  4. package/dist/chunk-CXBZ6WLZ.js.map +1 -0
  5. package/dist/index.d.ts +20 -24
  6. package/dist/index.js +269 -166
  7. package/dist/index.js.map +1 -1
  8. package/dist/plugins/truefoundry-agent-server-adapter/index.d.ts +82 -39
  9. package/dist/plugins/truefoundry-agent-server-adapter/index.js +3 -1
  10. package/dist/server/index.d.ts +2 -2
  11. package/dist/{types-BfiFf8O1.d.ts → types-B_z-FsDS.d.ts} +208 -10
  12. package/package.json +1 -1
  13. package/src/convertTurnMessages.ts +4 -0
  14. package/src/{private → draft}/agentSpec.ts +14 -17
  15. package/src/{private → draft}/draftSessionBridge.ts +1 -2
  16. package/src/{private → draft}/truefoundryDraftThreadListAdapter.test.ts +1 -1
  17. package/src/{private → draft}/truefoundryDraftThreadListAdapter.ts +6 -2
  18. package/src/{private → draft}/useDraftAgentSpec.ts +16 -5
  19. package/src/draftAgentConfig.test.ts +2 -1
  20. package/src/harness.temp.ts +85 -0
  21. package/src/index.ts +43 -7
  22. package/src/plugins/truefoundry-agent-server-adapter/README.md +83 -44
  23. package/src/plugins/truefoundry-agent-server-adapter/chatServer.ts +365 -0
  24. package/src/plugins/truefoundry-agent-server-adapter/cp.test.ts +444 -0
  25. package/src/plugins/truefoundry-agent-server-adapter/cp.ts +482 -0
  26. package/src/plugins/truefoundry-agent-server-adapter/createTrueFoundryAgentUIServer.ts +94 -0
  27. package/src/plugins/truefoundry-agent-server-adapter/guards.ts +1 -1
  28. package/src/plugins/truefoundry-agent-server-adapter/index.ts +20 -351
  29. package/src/plugins/truefoundry-agent-server-adapter/normalizeAgentSpec.test.ts +85 -0
  30. package/src/plugins/truefoundry-agent-server-adapter/normalizeAgentSpec.ts +84 -0
  31. package/src/plugins/truefoundry-agent-server-adapter/types.ts +7 -5
  32. package/src/server/index.ts +29 -0
  33. package/src/server/types.ts +264 -12
  34. package/src/streamTurn.test.ts +27 -27
  35. package/src/streamTurn.ts +2 -2
  36. package/src/truefoundryExtras.ts +4 -1
  37. package/src/truefoundryOwnedSessionsThreadListAdapter.ts +5 -2
  38. package/src/truefoundryThreadListAdapter.test.ts +22 -0
  39. package/src/truefoundryThreadListAdapter.ts +4 -1
  40. package/src/types.ts +1 -2
  41. package/src/useTrueFoundryAgentMessages.test.tsx +262 -2
  42. package/src/useTrueFoundryAgentMessages.ts +284 -176
  43. package/src/useTrueFoundryAgentRuntime.ts +31 -21
  44. package/dist/chunk-Q2SHKMLM.js +0 -270
  45. package/dist/chunk-Q2SHKMLM.js.map +0 -1
  46. /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, w as CreateSessionRequest, y as ListSessionsParams, S as Session, d as Turn, W as TurnState, a as AgentChatServer, a0 as UpdateSessionRequest, a2 as AgentSelectorEntry, a3 as ConnectorSelectorEntry, a4 as ModelSelectorEntry, a5 as SkillSelectorEntry, f as AgentBuilderServer } from '../../types-B_z-FsDS.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[];
@@ -80,11 +77,89 @@ type TfyFinishReason = TruefoundryGatewayApi.FinishReason;
80
77
  type TfyThreadState = TruefoundryGatewayApi.ThreadState;
81
78
  type TfyMcpServerInitInfo = TruefoundryGatewayApi.McpServerInitInfo;
82
79
 
80
+ type CreateTrueFoundryChatServerOptions = {
81
+ apiKey: string;
82
+ baseUrl: string;
83
+ /** Optional override — otherwise constructed from apiKey/baseUrl. */
84
+ client?: AgentSessionClient;
85
+ privateClient?: PrivateAgentSessionClient;
86
+ deleteSession?: (req: {
87
+ sessionId: string;
88
+ }) => Promise<void>;
89
+ };
90
+ /**
91
+ * Only the spec is generic. Session/Turn/list-params are the concrete Tfy*
92
+ * types because the adapter builds them as fixed object literals — a generic
93
+ * there would type fields that nothing ever populates. The spec is safe: the
94
+ * gateway SDK serializes with `unrecognizedObjectKeys: "passthrough"`, so
95
+ * host-added spec fields survive the round trip.
96
+ */
97
+ type TrueFoundryChatServer<TSpec extends TfyAgentSpec = TfyAgentSpec> = AgentChatServer<TSpec, TfySession<TSpec>, TfyCreateSessionRequest<TSpec>, TfyListSessionsParams, UpdateSessionRequest<TSpec>, TfyTurn> & {
98
+ /** Escape hatch for hosts that still need raw gateway clients. */
99
+ getGatewayClients(): {
100
+ client: AgentSessionClient;
101
+ privateClient: PrivateAgentSessionClient;
102
+ };
103
+ };
104
+ /**
105
+ * Wraps TrueFoundry gateway clients into a flat `AgentChatServer`.
106
+ * Named vs draft routing is fully internal — an in-memory session-type cache
107
+ * (populated by createSession/listSessions) determines which gateway client
108
+ * to call, falling back to a one-time probe for ids seen only in a URL.
109
+ */
110
+ declare function createTrueFoundryChatServer<TSpec extends TfyAgentSpec = TfyAgentSpec>(opts: CreateTrueFoundryChatServerOptions): TrueFoundryChatServer<TSpec>;
111
+
112
+ /**
113
+ * Control Plane HTTP for builder catalog lists + gateway URL resolution.
114
+ *
115
+ * Wire shapes are host/CP contracts (not Fern / gateway SDK). Paths match
116
+ * ai.tf cpApi usage — expect drift; keep normalizers defensive.
117
+ */
118
+
119
+ interface TfyModelSelectorEntry extends ModelSelectorEntry {
120
+ /** Write into AgentSpec.model.name (model_fqn). */
121
+ apiModel: string;
122
+ modelId: string;
123
+ providerAccount?: string;
124
+ id?: string;
125
+ }
126
+ interface TfySkillSelectorEntry extends SkillSelectorEntry {
127
+ /** Version FQN — mount as RegisteredSkillMount.fqn. */
128
+ fqn: string;
129
+ }
130
+ interface TfyConnectorSelectorEntry extends ConnectorSelectorEntry {
131
+ /** Mount as RegisteredMcpServer.name. */
132
+ mcpName: string;
133
+ serverId?: string | null;
134
+ authenticated?: boolean;
135
+ }
136
+ type TfyAgentSelectorEntry = AgentSelectorEntry;
137
+
138
+ type CreateTrueFoundryAgentUIServerOptions = {
139
+ apiKey: string;
140
+ /** Control Plane base URL (builder lists + optional /session for gateway resolve). */
141
+ cpURL: string;
142
+ /**
143
+ * Gateway base URL. When omitted, resolved via
144
+ * `GET {cpURL}/api/svc/v1/session` → `{cpURL}{env.LLM_GATEWAY_URL ?? "/api/llm"}/{tenantName}`.
145
+ * Session failure throws (no silent public-gateway fallback).
146
+ */
147
+ gatewayURL?: string;
148
+ };
149
+ type TrueFoundryAgentUIServer<TSpec extends TfyAgentSpec = TfyAgentSpec> = TrueFoundryChatServer<TSpec> & AgentBuilderServer<TSpec, TfyModelSelectorEntry, TfySkillSelectorEntry, TfyConnectorSelectorEntry, TfyAgentSelectorEntry, unknown>;
150
+ /**
151
+ * Full pack: gateway chat + Control Plane builder lists.
152
+ *
153
+ * Same `apiKey` bearer is used for CP and gateway. Concurrent calls with the
154
+ * same credentials share one in-flight promise (React Strict Mode safe).
155
+ */
156
+ declare function createTrueFoundryAgentUIServer<TSpec extends TfyAgentSpec = TfyAgentSpec>(opts: CreateTrueFoundryAgentUIServerOptions): Promise<TrueFoundryAgentUIServer<TSpec>>;
157
+
83
158
  /**
84
159
  * Point-of-use narrowing for the event half of the gateway protocol.
85
160
  *
86
161
  * `AgentChatServer` hardcodes the runtime's event types on listEvents,
87
- * listTurnEvents, subscribeToTurn and prepareAndExecuteTurn — there is no
162
+ * listTurnEvents, subscribeToTurn and createTurn — there is no
88
163
  * generic to override them from here. So instead of typing those channels,
89
164
  * hosts call these guards on the values they receive.
90
165
  *
@@ -127,36 +202,4 @@ declare function getTfyMcpInitServers(event: {
127
202
  mcpServers?: unknown;
128
203
  } | null | undefined): TfyMcpServerInitInfo[] | undefined;
129
204
 
130
- type CreateTrueFoundryChatServerOptions = {
131
- apiKey: string;
132
- baseUrl: string;
133
- /** Optional override — otherwise constructed from apiKey/baseUrl. */
134
- client?: AgentSessionClient;
135
- privateClient?: PrivateAgentSessionClient;
136
- deleteSession?: (req: {
137
- sessionId: string;
138
- }) => Promise<void>;
139
- };
140
- /**
141
- * Only the spec is generic. Session/Turn/list-params are the concrete Tfy*
142
- * types because the adapter builds them as fixed object literals — a generic
143
- * there would type fields that nothing ever populates. The spec is safe: the
144
- * gateway SDK serializes with `unrecognizedObjectKeys: "passthrough"`, so
145
- * host-added spec fields survive the round trip.
146
- */
147
- type TrueFoundryChatServer<TSpec extends TfyAgentSpec = TfyAgentSpec> = AgentChatServer<TSpec, TfySession<TSpec>, TfyCreateSessionRequest<TSpec>, TfyListSessionsParams, UpdateSessionRequest<TSpec>, TfyTurn> & {
148
- /** Escape hatch for hosts that still need raw gateway clients. */
149
- getGatewayClients(): {
150
- client: AgentSessionClient;
151
- privateClient: PrivateAgentSessionClient;
152
- };
153
- };
154
- /**
155
- * Wraps TrueFoundry gateway clients into a flat `AgentChatServer`.
156
- * Named vs draft routing is fully internal — an in-memory session-type cache
157
- * (populated by createSession/listSessions) determines which gateway client
158
- * to call. No try/catch fallback, no double network calls.
159
- */
160
- declare function createTrueFoundryChatServer<TSpec extends TfyAgentSpec = TfyAgentSpec>(opts: CreateTrueFoundryChatServerOptions): TrueFoundryChatServer<TSpec>;
161
-
162
- export { type CreateTrueFoundryChatServerOptions, type RequireApprovalToolSelectorItem, type RequireApprovalToolsSelectorTag, type TfyAgentSpec, type TfyCreateSessionRequest, type TfyFinishReason, type TfyListSessionsParams, type TfyMcpServerInitInfo, type TfyMcpServerMount, type TfyMcpToolInfo, type TfyModelMessageUsage, type TfyModelParams, type TfyResponseFormat, type TfyRuntimeConfig, type TfySession, type TfySkillMount, type TfySubject, type TfySystemToolInfo, type TfyThreadState, type TfyToolInfo, type TfyTurn, type TfyTurnCancelledReason, type TfyTurnState, type TfyTurnStateDoneOutput, type ToolsSelectorItem, type ToolsSelectorTag, type TrueFoundryChatServer, createTrueFoundryChatServer, getTfyMcpInitServers, getTfyThreadState, getTfyUsage, isTfyMcpToolInfo, isTfySystemToolInfo, isTfyToolInfo };
205
+ export { type CreateTrueFoundryAgentUIServerOptions, type CreateTrueFoundryChatServerOptions, type RequireApprovalToolSelectorItem, type RequireApprovalToolsSelectorTag, type TfyAgentSelectorEntry, type TfyAgentSpec, type TfyConnectorSelectorEntry, type TfyCreateSessionRequest, type TfyFinishReason, type TfyListSessionsParams, type TfyMcpServerInitInfo, type TfyMcpServerMount, type TfyMcpToolInfo, type TfyModelMessageUsage, type TfyModelParams, type TfyModelSelectorEntry, type TfyResponseFormat, type TfyRuntimeConfig, type TfySession, type TfySkillMount, type TfySkillSelectorEntry, type TfySubject, type TfySystemToolInfo, type TfyThreadState, type TfyToolInfo, type TfyTurn, type TfyTurnCancelledReason, type TfyTurnState, type TfyTurnStateDoneOutput, type ToolsSelectorItem, type ToolsSelectorTag, type TrueFoundryAgentUIServer, type TrueFoundryChatServer, createTrueFoundryAgentUIServer, createTrueFoundryChatServer, getTfyMcpInitServers, getTfyThreadState, getTfyUsage, isTfyMcpToolInfo, isTfySystemToolInfo, isTfyToolInfo };
@@ -1,4 +1,5 @@
1
1
  import {
2
+ createTrueFoundryAgentUIServer,
2
3
  createTrueFoundryChatServer,
3
4
  getTfyMcpInitServers,
4
5
  getTfyThreadState,
@@ -6,8 +7,9 @@ import {
6
7
  isTfyMcpToolInfo,
7
8
  isTfySystemToolInfo,
8
9
  isTfyToolInfo
9
- } from "../../chunk-Q2SHKMLM.js";
10
+ } from "../../chunk-CXBZ6WLZ.js";
10
11
  export {
12
+ createTrueFoundryAgentUIServer,
11
13
  createTrueFoundryChatServer,
12
14
  getTfyMcpInitServers,
13
15
  getTfyThreadState,
@@ -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 { Z as TurnStreamingEvent, a6 as DeltaEvents, T as TurnEvent } from '../types-B_z-FsDS.js';
2
+ export { a7 as ActionRequiredEvent, f as AgentBuilderServer, a as AgentChatServer, a8 as AgentInfo, a9 as AgentParent, a2 as AgentSelectorEntry, A as AgentSpec, g as AgentUIServerPort, aa as ApprovalDecision, C as CatalogServer, ab as ChunkDeltaToolCall, h as ConnectorAuth, i as ConnectorAuthApiKey, j as ConnectorAuthNone, k as ConnectorAuthOAuth, l as ConnectorAuthPublic, m as ConnectorAuthPublicApiKey, n as ConnectorAuthPublicNone, o as ConnectorAuthPublicOAuth, p as ConnectorAuthType, q as ConnectorBase, r as ConnectorCatalogEntry, s as ConnectorCatalogServer, t as ConnectorConfigBase, a3 as ConnectorSelectorEntry, u as CreateConnectorRequest, v as CreateModelProviderRequest, w as CreateSessionRequest, x as CreateSkillRequest, L as ListResult, ac as ListSessionsOrder, y as ListSessionsParams, M as McpAuthRequiredEvent, ad as McpInitializeEvent, ae as McpServerAuthInfo, af as McpServerMount, ag as Model, z as ModelCatalogServer, B as ModelEntry, ah as ModelMessageContentPart, ai as ModelMessageDeltaEvent, D as ModelMessageEvent, aj as ModelParams, E as ModelProviderBase, F as ModelProviderCatalogEntry, G as ModelProviderConfigBase, a4 as ModelSelectorEntry, ak as PageParams, P as PreviousTurnIdInput, H as ProviderType, I as SandboxCreatedEvent, al as SearchAgentSelectorParams, S as Session, J as SessionEventItem, K as SkillBase, N as SkillCatalogServer, am as SkillMount, a5 as SkillSelectorEntry, b as ThreadCreatedEvent, an as ThreadDoneEvent, O as ToolApprovalRequiredEvent, Q as ToolBase, R as ToolCall, ao as ToolCallFunction, ap as ToolCallRef, aq as ToolInfo, ar as ToolResponseEvent, V as ToolResponseRequiredEvent, d as Turn, as as TurnCreatedEvent, at as TurnDoneEvent, e as TurnInputItem, W as TurnState, au as TurnStateCancelled, X as TurnStateDone, av as TurnStateError, aw as TurnStateRunning, Y as TurnStreamData, _ as UpdateConnectorRequest, $ as UpdateModelProviderRequest, a0 as UpdateSessionRequest, a1 as UserMessage, ax as UserMessageContent, c as UserToolApprovalEvent, U as UserToolResponseEvent } from '../types-B_z-FsDS.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;
@@ -400,7 +398,7 @@ interface AgentChatServer<TSpec extends AgentSpec = AgentSpec, TSession extends
400
398
  sessionId: string;
401
399
  }): Promise<TSession>;
402
400
  updateSession(req: TUpdate): Promise<TSession>;
403
- prepareAndExecuteTurn(req: {
401
+ createTurn(req: {
404
402
  sessionId: string;
405
403
  input?: TurnInputItem[];
406
404
  previousTurnId?: PreviousTurnIdInput;
@@ -464,5 +462,205 @@ 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
+ /** Strict auth type id. Hosts widen branches via intersection + re-union. */
537
+ type ConnectorAuthType = "oauth" | "apiKey" | "none";
538
+ type ConnectorAuthOAuth = {
539
+ type: "oauth";
540
+ authUrl?: string;
541
+ };
542
+ type ConnectorAuthApiKey = {
543
+ type: "apiKey";
544
+ apiKey?: string;
545
+ headerName?: string;
546
+ };
547
+ type ConnectorAuthNone = {
548
+ type: "none";
549
+ };
550
+ type ConnectorAuth = ConnectorAuthOAuth | ConnectorAuthApiKey | ConnectorAuthNone;
551
+ type ConnectorAuthPublicOAuth = {
552
+ type: "oauth";
553
+ authUrl: string;
554
+ };
555
+ type ConnectorAuthPublicApiKey = {
556
+ type: "apiKey";
557
+ headerName?: string;
558
+ };
559
+ type ConnectorAuthPublicNone = {
560
+ type: "none";
561
+ };
562
+ type ConnectorAuthPublic = ConnectorAuthPublicOAuth | ConnectorAuthPublicApiKey | ConnectorAuthPublicNone;
563
+ /**
564
+ * MCP / connector create-edit config. Host extends for extra fields, etc.
565
+ */
566
+ interface ConnectorConfigBase<TAuth extends ConnectorAuth = ConnectorAuth> {
567
+ name: string;
568
+ url: string;
569
+ auth: TAuth;
570
+ }
571
+ /**
572
+ * Connected connector row (settings/connectors). No raw `apiKey`.
573
+ * Host extends.
574
+ */
575
+ interface ConnectorBase<TTool extends ToolBase = ToolBase, TAuth extends ConnectorAuthPublic = ConnectorAuthPublic> {
576
+ id: string;
577
+ name: string;
578
+ description: string;
579
+ url: string;
580
+ auth: TAuth;
581
+ /** When true, UI should not show Disconnect. */
582
+ requiresAuth: boolean;
583
+ authenticated: boolean;
584
+ tools: TTool[];
585
+ }
586
+ /** Discovery catalog entry for "+ Add MCP server". Host extends. */
587
+ interface ConnectorCatalogEntry<TAuth extends ConnectorAuthPublic = ConnectorAuthPublic> {
588
+ id: string;
589
+ name: string;
590
+ description?: string;
591
+ url: string;
592
+ auth: TAuth;
593
+ }
594
+ /** Create connector — no `id`; server assigns it. Host extends. */
595
+ type CreateConnectorRequest<TAuth extends ConnectorAuth = ConnectorAuth> = ConnectorConfigBase<TAuth>;
596
+ /** Update connector — `id` required. Host extends. */
597
+ type UpdateConnectorRequest<TAuth extends ConnectorAuth = ConnectorAuth> = ConnectorConfigBase<TAuth> & {
598
+ id: string;
599
+ };
600
+ 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>> {
601
+ getConnectorCatalog(): Promise<TCatalogEntry[]>;
602
+ listConnectors(req?: {
603
+ query?: string;
604
+ }): Promise<TConnector[]>;
605
+ createConnector(req: TCreate): Promise<TConnector>;
606
+ /** Full replace update keyed by connector `id`. */
607
+ updateConnector(req: TUpdate): Promise<TConnector>;
608
+ /**
609
+ * Start connector auth (e.g. OAuth).
610
+ * For oauth, the returned connector's `auth.authUrl` is the authorize URL.
611
+ */
612
+ authenticateConnector(req: {
613
+ id: string;
614
+ }): Promise<TConnector>;
615
+ /** Clear connector auth. */
616
+ disconnectConnector(req: {
617
+ id: string;
618
+ }): Promise<TConnector>;
619
+ deleteConnector?(req: {
620
+ id: string;
621
+ }): Promise<void>;
622
+ }
623
+ /** Skill row shown in settings/skills (list + delete). Host extends for fqn, etc. */
624
+ interface SkillBase {
625
+ id: string;
626
+ name: string;
627
+ description: string;
628
+ }
629
+ /** Create-skill request. Host extends for branch, auth, etc. */
630
+ interface CreateSkillRequest {
631
+ repo: string;
632
+ directory: string;
633
+ }
634
+ interface SkillCatalogServer<TSkill extends SkillBase = SkillBase, TCreate extends CreateSkillRequest = CreateSkillRequest> {
635
+ listSkills(req?: {
636
+ query?: string;
637
+ }): Promise<TSkill[]>;
638
+ createSkill(req: TCreate): Promise<TSkill>;
639
+ deleteSkill?(req: {
640
+ id: string;
641
+ }): Promise<void>;
642
+ }
643
+ /**
644
+ * Settings management aggregate — modelCatalog + connectorCatalog + optional skillCatalog.
645
+ * Hosts may pass the whole object to an app shell, or a focused sub-port to a page.
646
+ */
647
+ interface CatalogServer<TModelCatalog extends ModelCatalogServer = ModelCatalogServer, TConnectorCatalog extends ConnectorCatalogServer = ConnectorCatalogServer, TSkillCatalog extends SkillCatalogServer = SkillCatalogServer> {
648
+ modelCatalog: TModelCatalog;
649
+ connectorCatalog: TConnectorCatalog;
650
+ /** Optional — omit when the host has no skills settings surface. */
651
+ skillCatalog?: TSkillCatalog;
652
+ }
653
+ /**
654
+ * Composed host port: chat + builder + optional settings catalog.
655
+ * Agent-ui's `AgentUIServer` mirrors this shape; named differently here to
656
+ * avoid colliding with that package's local type name.
657
+ *
658
+ * `catalog` is optional — if the host passes it, settings UI can call
659
+ * `useCatalogServer()` / show modelCatalog, connectorCatalog, and skillCatalog;
660
+ * if omitted, those surfaces stay hidden.
661
+ */
662
+ type AgentUIServerPort<TChat extends AgentChatServer = AgentChatServer, TBuilder extends AgentBuilderServer = AgentBuilderServer, TCatalog extends CatalogServer = CatalogServer> = TChat & TBuilder & {
663
+ catalog?: TCatalog;
664
+ };
467
665
 
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 };
666
+ export type { UpdateModelProviderRequest as $, AgentSpec as A, ModelEntry as B, CatalogServer as C, ModelMessageEvent as D, ModelProviderBase as E, ModelProviderCatalogEntry as F, ModelProviderConfigBase as G, ProviderType as H, SandboxCreatedEvent as I, SessionEventItem as J, SkillBase as K, ListResult as L, McpAuthRequiredEvent as M, SkillCatalogServer as N, ToolApprovalRequiredEvent as O, PreviousTurnIdInput as P, ToolBase as Q, ToolCall as R, Session as S, TurnEvent as T, UserToolResponseEvent as U, ToolResponseRequiredEvent as V, TurnState as W, TurnStateDone as X, TurnStreamData as Y, TurnStreamingEvent as Z, UpdateConnectorRequest as _, AgentChatServer as a, UpdateSessionRequest as a0, UserMessage as a1, AgentSelectorEntry as a2, ConnectorSelectorEntry as a3, ModelSelectorEntry as a4, SkillSelectorEntry as a5, DeltaEvents as a6, ActionRequiredEvent as a7, AgentInfo as a8, AgentParent as a9, ApprovalDecision as aa, ChunkDeltaToolCall as ab, ListSessionsOrder as ac, McpInitializeEvent as ad, McpServerAuthInfo as ae, McpServerMount as af, Model as ag, ModelMessageContentPart as ah, ModelMessageDeltaEvent as ai, ModelParams as aj, PageParams as ak, SearchAgentSelectorParams as al, SkillMount as am, ThreadDoneEvent as an, ToolCallFunction as ao, ToolCallRef as ap, ToolInfo as aq, ToolResponseEvent as ar, TurnCreatedEvent as as, TurnDoneEvent as at, TurnStateCancelled as au, TurnStateError as av, TurnStateRunning as aw, UserMessageContent as ax, ThreadCreatedEvent as b, UserToolApprovalEvent as c, Turn as d, TurnInputItem as e, AgentBuilderServer as f, AgentUIServerPort as g, ConnectorAuth as h, ConnectorAuthApiKey as i, ConnectorAuthNone as j, ConnectorAuthOAuth as k, ConnectorAuthPublic as l, ConnectorAuthPublicApiKey as m, ConnectorAuthPublicNone as n, ConnectorAuthPublicOAuth as o, ConnectorAuthType as p, ConnectorBase as q, ConnectorCatalogEntry as r, ConnectorCatalogServer as s, ConnectorConfigBase as t, CreateConnectorRequest as u, CreateModelProviderRequest as v, CreateSessionRequest as w, CreateSkillRequest as x, ListSessionsParams as y, ModelCatalogServer 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.8",
4
4
  "description": "TrueFoundry Gateway agent runtime adapter for assistant-ui",
5
5
  "license": "Apache-2.0",
6
6
  "repository": {
@@ -516,6 +516,10 @@ function attachRunningTurn(
516
516
  if (runningTurn == null) {
517
517
  return snapshot;
518
518
  }
519
+ // Session-level history excludes the running turn. Apply continuation inputs
520
+ // from the turn listing so answered approvals / ask-user prompts are not
521
+ // restored as pending while reconnecting to that turn after a refresh.
522
+ applyUserToolResponsesToFold(snapshot.fold, runningTurn.input ?? []);
519
523
  const pendingUserText = extractTurnUserText(runningTurn.input);
520
524
  return replaceSessionSnapshot(snapshot, {
521
525
  runningTurn,
@@ -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;
@@ -60,7 +61,10 @@ export function createTrueFoundryDraftThreadListAdapter(options: {
60
61
  async rename() {},
61
62
  async archive() {},
62
63
  async unarchive() {},
63
- async delete() {},
64
+ async delete(remoteId) {
65
+ if (typeof server.deleteSession !== "function") return;
66
+ await server.deleteSession({ sessionId: remoteId });
67
+ },
64
68
 
65
69
  async generateTitle() {
66
70
  return new ReadableStream();
@@ -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,