@truefoundry/assistant-ui-runtime 0.1.13 → 0.1.14

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.
@@ -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, K as CreateSessionRequest, W as ListSessionsParams, S as Session, d as Turn, az as TurnState, a as AgentChatServer, aH as UpdateSessionRequest, i as AgentSelectorEntry, E as ConnectorSelectorEntry, a6 as ModelSelectorEntry, au as SkillSelectorEntry, g as AgentBuilderServer } from '../../types-BE-F3vCD.js';
3
+ import { A as AgentSpec, N as CreateSessionRequest, Y as ListSessionsParams, S as Session, d as Turn, aC as TurnState, a as AgentChatServer, aK as UpdateSessionRequest, k as AgentSelectorEntry, G as ConnectorSelectorEntry, a8 as ModelSelectorEntry, ax as SkillSelectorEntry, g as AgentBuilderServer } from '../../types-VU3Fc17W.js';
4
4
  import { TruefoundryGatewayApi } from 'truefoundry-gateway-sdk';
5
5
 
6
6
  /**
@@ -29,8 +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<TruefoundryGatewayApi.Model, TfySkillMount, TfyMcpServerMount> {
33
- config?: TruefoundryGatewayApi.RuntimeConfig;
32
+ interface TfyAgentSpec extends AgentSpec<TruefoundryGatewayApi.Model, TfySkillMount, TfyMcpServerMount, TruefoundryGatewayApi.RuntimeConfig> {
34
33
  responseFormat?: TruefoundryGatewayApi.ResponseFormat;
35
34
  messages?: TruefoundryGatewayApi.AgentSpecUserMessage[];
36
35
  }
@@ -7,7 +7,7 @@ import {
7
7
  isTfyMcpToolInfo,
8
8
  isTfySystemToolInfo,
9
9
  isTfyToolInfo
10
- } from "../../chunk-XYGEKRA2.js";
10
+ } from "../../chunk-S4SH2KT2.js";
11
11
  export {
12
12
  createTrueFoundryAgentUIServer,
13
13
  createTrueFoundryChatServer,
@@ -1,5 +1,5 @@
1
- import { aC as TurnStreamingEvent, aJ as DeltaEvents, T as TurnEvent } from '../types-BE-F3vCD.js';
2
- export { aK as ActionRequiredEvent, f as AgentBuilderCapabilitiesResponse, g as AgentBuilderServer, a as AgentChatServer, aL as AgentInfo, h as AgentLibraryEntry, aM as AgentParent, i as AgentSelectorEntry, j as AgentSkill, A as AgentSpec, k as AgentUIServer, l as AgentUIServerPort, m as ApprovalDecision, n as AuthenticateConnectorRequest, C as CatalogServer, aN as ChunkDeltaToolCall, o as ConnectorAuth, p as ConnectorAuthApiKey, q as ConnectorAuthNone, r as ConnectorAuthOAuth, s as ConnectorAuthPublic, t as ConnectorAuthPublicApiKey, u as ConnectorAuthPublicNone, v as ConnectorAuthPublicOAuth, w as ConnectorAuthType, x as ConnectorAuthenticationResult, y as ConnectorBase, z as ConnectorCatalogEntry, B as ConnectorCatalogServer, D as ConnectorConfigBase, E as ConnectorSelectorEntry, F as ConnectorState, G as CreateConnectorRequest, H as CreateModelProviderRequest, I as CreateSandboxProviderRequest, J as CreateSandboxRequest, K as CreateSessionRequest, L as CreateSkillRequest, N as CreateSkillRequestBase, O as DefinedSkill, P as GithubSkill, Q as ImportGithubSkillRequest, R as ListResult, V as ListSessionsOrder, W as ListSessionsParams, M as McpAuthRequiredEvent, aO as McpInitializeEvent, aP as McpServerAuthInfo, X as McpServerMount, Y as Model, Z as ModelCatalogServer, _ as ModelEntry, aQ as ModelMessageContentPart, aR as ModelMessageDeltaEvent, $ as ModelMessageEvent, a0 as ModelParams, a1 as ModelProperties, a2 as ModelProviderBase, a3 as ModelProviderCatalogEntry, a4 as ModelProviderConfigBase, a5 as ModelSelection, a6 as ModelSelectorEntry, a7 as PageParams, a8 as PreviousTurnIdInput, a9 as ProviderEntry, aa as ProviderType, ab as RegistrySkill, ac as SandboxBase, ad as SandboxCatalogEntry, ae as SandboxCatalogServer, af as SandboxConfig, ag as SandboxCreatedEvent, ah as SandboxProviderBase, ai as SandboxProviderCatalogEntry, aj as SandboxProviderConfig, ak as SaveAgentRequest, al as SearchAgentSelectorParams, am as SearchAgentsParams, an as SelectRegistrySkillRequest, S as Session, ao as SessionEventItem, ap as SkillBase, aq as SkillCatalogEntry, ar as SkillCatalogServer, as as SkillConfigBase, at as SkillMount, au as SkillSelectorEntry, b as ThreadCreatedEvent, aS as ThreadDoneEvent, av as ToolApprovalRequiredEvent, aw as ToolBase, ax as ToolCall, aT as ToolCallFunction, aU as ToolCallRef, aV as ToolInfo, aW as ToolResponseEvent, ay as ToolResponseRequiredEvent, d as Turn, aX as TurnCreatedEvent, aY as TurnDoneEvent, e as TurnInputItem, az as TurnState, aZ as TurnStateCancelled, aA as TurnStateDone, a_ as TurnStateError, a$ as TurnStateRunning, aB as TurnStreamData, aD as UpdateConnectorRequest, aE as UpdateModelProviderRequest, aF as UpdateSandboxProviderRequest, aG as UpdateSandboxRequest, aH as UpdateSessionRequest, aI as UserMessage, b0 as UserMessageContent, c as UserToolApprovalEvent, U as UserToolResponseEvent } from '../types-BE-F3vCD.js';
1
+ import { aF as TurnStreamingEvent, aM as DeltaEvents, T as TurnEvent } from '../types-VU3Fc17W.js';
2
+ export { aN as ActionRequiredEvent, f as AgentBuilderCapabilitiesResponse, g as AgentBuilderServer, h as AgentCapabilityConfig, a as AgentChatServer, aO as AgentInfo, i as AgentLibraryEntry, aP as AgentParent, j as AgentRuntimeConfig, k as AgentSelectorEntry, l as AgentSkill, A as AgentSpec, m as AgentUIServer, n as AgentUIServerPort, o as ApprovalDecision, p as AuthenticateConnectorRequest, C as CatalogServer, aQ as ChunkDeltaToolCall, q as ConnectorAuth, r as ConnectorAuthApiKey, s as ConnectorAuthNone, t as ConnectorAuthOAuth, u as ConnectorAuthPublic, v as ConnectorAuthPublicApiKey, w as ConnectorAuthPublicNone, x as ConnectorAuthPublicOAuth, y as ConnectorAuthType, z as ConnectorAuthenticationResult, B as ConnectorBase, D as ConnectorCatalogEntry, E as ConnectorCatalogServer, F as ConnectorConfigBase, G as ConnectorSelectorEntry, H as ConnectorState, I as CreateConnectorRequest, J as CreateModelProviderRequest, K as CreateSandboxProviderRequest, L as CreateSandboxRequest, N as CreateSessionRequest, O as CreateSkillRequest, P as CreateSkillRequestBase, Q as DefinedSkill, R as GithubSkill, V as ImportGithubSkillRequest, W as ListResult, X as ListSessionsOrder, Y as ListSessionsParams, M as McpAuthRequiredEvent, aR as McpInitializeEvent, aS as McpServerAuthInfo, Z as McpServerMount, _ as Model, $ as ModelCatalogServer, a0 as ModelEntry, aT as ModelMessageContentPart, aU as ModelMessageDeltaEvent, a1 as ModelMessageEvent, a2 as ModelParams, a3 as ModelProperties, a4 as ModelProviderBase, a5 as ModelProviderCatalogEntry, a6 as ModelProviderConfigBase, a7 as ModelSelection, a8 as ModelSelectorEntry, a9 as PageParams, aa as PreviousTurnIdInput, ab as ProviderEntry, ac as ProviderType, ad as RegistrySkill, ae as SandboxBase, af as SandboxCatalogEntry, ag as SandboxCatalogServer, ah as SandboxConfig, ai as SandboxCreatedEvent, aj as SandboxProviderBase, ak as SandboxProviderCatalogEntry, al as SandboxProviderConfig, am as SaveAgentRequest, an as SaveAgentResult, ao as SearchAgentSelectorParams, ap as SearchAgentsParams, aq as SelectRegistrySkillRequest, S as Session, ar as SessionEventItem, as as SkillBase, at as SkillCatalogEntry, au as SkillCatalogServer, av as SkillConfigBase, aw as SkillMount, ax as SkillSelectorEntry, b as ThreadCreatedEvent, aV as ThreadDoneEvent, ay as ToolApprovalRequiredEvent, az as ToolBase, aA as ToolCall, aW as ToolCallFunction, aX as ToolCallRef, aY as ToolInfo, aZ as ToolResponseEvent, aB as ToolResponseRequiredEvent, d as Turn, a_ as TurnCreatedEvent, a$ as TurnDoneEvent, e as TurnInputItem, aC as TurnState, b0 as TurnStateCancelled, aD as TurnStateDone, b1 as TurnStateError, b2 as TurnStateRunning, aE as TurnStreamData, aG as UpdateConnectorRequest, aH as UpdateModelProviderRequest, aI as UpdateSandboxProviderRequest, aJ as UpdateSandboxRequest, aK as UpdateSessionRequest, aL as UserMessage, b3 as UserMessageContent, c as UserToolApprovalEvent, U as UserToolResponseEvent } from '../types-VU3Fc17W.js';
3
3
 
4
4
  /**
5
5
  * Local implementations of streaming delta helpers.
@@ -294,15 +294,24 @@ interface Model {
294
294
  name: string;
295
295
  params?: ModelParams;
296
296
  }
297
+ interface AgentCapabilityConfig {
298
+ enabled?: boolean;
299
+ }
300
+ interface AgentRuntimeConfig {
301
+ generativeUi?: AgentCapabilityConfig;
302
+ dynamicSubAgents?: AgentCapabilityConfig;
303
+ askUserQuestions?: AgentCapabilityConfig;
304
+ }
297
305
  /**
298
306
  * SDK-owned agent definition — fields the FE reads/writes.
299
- * Host widens `model` / `skills` / `mcpServers` via type params, and adds
300
- * extra fields via `TSpec extends AgentSpec<...>`.
307
+ * Host widens `model` / `skills` / `mcpServers` / `config` via type params,
308
+ * and adds extra fields via `TSpec extends AgentSpec<...>`.
301
309
  */
302
- interface AgentSpec<TModel extends Model = Model, TSkill extends SkillMount = SkillMount, TMcp extends McpServerMount = McpServerMount> {
310
+ interface AgentSpec<TModel extends Model = Model, TSkill extends SkillMount = SkillMount, TMcp extends McpServerMount = McpServerMount, TConfig extends AgentRuntimeConfig = AgentRuntimeConfig> {
303
311
  model: TModel;
304
312
  skills?: TSkill[];
305
313
  mcpServers?: TMcp[];
314
+ config?: TConfig;
306
315
  instructions?: string;
307
316
  variables?: Record<string, string>;
308
317
  }
@@ -475,6 +484,15 @@ interface AgentChatServer<TSpec extends AgentSpec = AgentSpec, TSession extends
475
484
  interface SaveAgentRequest<TSpec extends AgentSpec = AgentSpec> {
476
485
  agentName: string;
477
486
  agentSpec: TSpec;
487
+ intent: "create" | "update";
488
+ /** Current mutable session to update atomically with the named agent. */
489
+ sessionId?: string;
490
+ }
491
+ interface SaveAgentResult {
492
+ /** Immutable id allocated by the host registry. */
493
+ agentId?: string;
494
+ /** Timestamp returned when the active mutable session was updated. */
495
+ sessionUpdatedAt?: string;
478
496
  }
479
497
  /**
480
498
  * Builder feature flags — atoms gate sandbox / skill / settings surfaces on these.
@@ -497,7 +515,7 @@ interface AgentBuilderCapabilitiesResponse {
497
515
  * Builder catalog + persist port — atoms call these.
498
516
  * Passed separately from the runtime's chat server.
499
517
  */
500
- interface AgentBuilderServer<TSpec extends AgentSpec = AgentSpec, TModel extends ModelSelectorEntry = ModelSelectorEntry, TSkill extends SkillSelectorEntry = SkillSelectorEntry, TMcp extends ConnectorSelectorEntry = ConnectorSelectorEntry, TAgent extends AgentSelectorEntry = AgentSelectorEntry, TSave = unknown, TCapabilities extends AgentBuilderCapabilitiesResponse = AgentBuilderCapabilitiesResponse> {
518
+ interface AgentBuilderServer<TSpec extends AgentSpec = AgentSpec, TModel extends ModelSelectorEntry = ModelSelectorEntry, TSkill extends SkillSelectorEntry = SkillSelectorEntry, TMcp extends ConnectorSelectorEntry = ConnectorSelectorEntry, TAgent extends AgentSelectorEntry = AgentSelectorEntry, TSave = SaveAgentResult, TCapabilities extends AgentBuilderCapabilitiesResponse = AgentBuilderCapabilitiesResponse> {
501
519
  getCapabilities(): Promise<TCapabilities>;
502
520
  getModels(): Promise<TModel[]>;
503
521
  getSkills(): Promise<TSkill[]>;
@@ -815,4 +833,4 @@ type AgentUIServerPort<TChat extends AgentChatServer = AgentChatServer, TBuilder
815
833
  /** Host-facing alias used by trueforge-ui. */
816
834
  type AgentUIServer = AgentUIServerPort;
817
835
 
818
- export type { ModelMessageEvent as $, AgentSpec as A, ConnectorCatalogServer as B, CatalogServer as C, ConnectorConfigBase as D, ConnectorSelectorEntry as E, ConnectorState as F, CreateConnectorRequest as G, CreateModelProviderRequest as H, CreateSandboxProviderRequest as I, CreateSandboxRequest as J, CreateSessionRequest as K, CreateSkillRequest as L, McpAuthRequiredEvent as M, CreateSkillRequestBase as N, DefinedSkill as O, GithubSkill as P, ImportGithubSkillRequest as Q, ListResult as R, Session as S, TurnEvent as T, UserToolResponseEvent as U, ListSessionsOrder as V, ListSessionsParams as W, McpServerMount as X, Model as Y, ModelCatalogServer as Z, ModelEntry as _, AgentChatServer as a, TurnStateRunning as a$, ModelParams as a0, ModelProperties as a1, ModelProviderBase as a2, ModelProviderCatalogEntry as a3, ModelProviderConfigBase as a4, ModelSelection as a5, ModelSelectorEntry as a6, PageParams as a7, PreviousTurnIdInput as a8, ProviderEntry as a9, TurnStateDone as aA, TurnStreamData as aB, TurnStreamingEvent as aC, UpdateConnectorRequest as aD, UpdateModelProviderRequest as aE, UpdateSandboxProviderRequest as aF, UpdateSandboxRequest as aG, UpdateSessionRequest as aH, UserMessage as aI, DeltaEvents as aJ, ActionRequiredEvent as aK, AgentInfo as aL, AgentParent as aM, ChunkDeltaToolCall as aN, McpInitializeEvent as aO, McpServerAuthInfo as aP, ModelMessageContentPart as aQ, ModelMessageDeltaEvent as aR, ThreadDoneEvent as aS, ToolCallFunction as aT, ToolCallRef as aU, ToolInfo as aV, ToolResponseEvent as aW, TurnCreatedEvent as aX, TurnDoneEvent as aY, TurnStateCancelled as aZ, TurnStateError as a_, ProviderType as aa, RegistrySkill as ab, SandboxBase as ac, SandboxCatalogEntry as ad, SandboxCatalogServer as ae, SandboxConfig as af, SandboxCreatedEvent as ag, SandboxProviderBase as ah, SandboxProviderCatalogEntry as ai, SandboxProviderConfig as aj, SaveAgentRequest as ak, SearchAgentSelectorParams as al, SearchAgentsParams as am, SelectRegistrySkillRequest as an, SessionEventItem as ao, SkillBase as ap, SkillCatalogEntry as aq, SkillCatalogServer as ar, SkillConfigBase as as, SkillMount as at, SkillSelectorEntry as au, ToolApprovalRequiredEvent as av, ToolBase as aw, ToolCall as ax, ToolResponseRequiredEvent as ay, TurnState as az, ThreadCreatedEvent as b, UserMessageContent as b0, UserToolApprovalEvent as c, Turn as d, TurnInputItem as e, AgentBuilderCapabilitiesResponse as f, AgentBuilderServer as g, AgentLibraryEntry as h, AgentSelectorEntry as i, AgentSkill as j, AgentUIServer as k, AgentUIServerPort as l, ApprovalDecision as m, AuthenticateConnectorRequest as n, ConnectorAuth as o, ConnectorAuthApiKey as p, ConnectorAuthNone as q, ConnectorAuthOAuth as r, ConnectorAuthPublic as s, ConnectorAuthPublicApiKey as t, ConnectorAuthPublicNone as u, ConnectorAuthPublicOAuth as v, ConnectorAuthType as w, ConnectorAuthenticationResult as x, ConnectorBase as y, ConnectorCatalogEntry as z };
836
+ export type { ModelCatalogServer as $, AgentSpec as A, ConnectorBase as B, CatalogServer as C, ConnectorCatalogEntry as D, ConnectorCatalogServer as E, ConnectorConfigBase as F, ConnectorSelectorEntry as G, ConnectorState as H, CreateConnectorRequest as I, CreateModelProviderRequest as J, CreateSandboxProviderRequest as K, CreateSandboxRequest as L, McpAuthRequiredEvent as M, CreateSessionRequest as N, CreateSkillRequest as O, CreateSkillRequestBase as P, DefinedSkill as Q, GithubSkill as R, Session as S, TurnEvent as T, UserToolResponseEvent as U, ImportGithubSkillRequest as V, ListResult as W, ListSessionsOrder as X, ListSessionsParams as Y, McpServerMount as Z, Model as _, AgentChatServer as a, TurnDoneEvent as a$, ModelEntry as a0, ModelMessageEvent as a1, ModelParams as a2, ModelProperties as a3, ModelProviderBase as a4, ModelProviderCatalogEntry as a5, ModelProviderConfigBase as a6, ModelSelection as a7, ModelSelectorEntry as a8, PageParams as a9, ToolCall as aA, ToolResponseRequiredEvent as aB, TurnState as aC, TurnStateDone as aD, TurnStreamData as aE, TurnStreamingEvent as aF, UpdateConnectorRequest as aG, UpdateModelProviderRequest as aH, UpdateSandboxProviderRequest as aI, UpdateSandboxRequest as aJ, UpdateSessionRequest as aK, UserMessage as aL, DeltaEvents as aM, ActionRequiredEvent as aN, AgentInfo as aO, AgentParent as aP, ChunkDeltaToolCall as aQ, McpInitializeEvent as aR, McpServerAuthInfo as aS, ModelMessageContentPart as aT, ModelMessageDeltaEvent as aU, ThreadDoneEvent as aV, ToolCallFunction as aW, ToolCallRef as aX, ToolInfo as aY, ToolResponseEvent as aZ, TurnCreatedEvent as a_, PreviousTurnIdInput as aa, ProviderEntry as ab, ProviderType as ac, RegistrySkill as ad, SandboxBase as ae, SandboxCatalogEntry as af, SandboxCatalogServer as ag, SandboxConfig as ah, SandboxCreatedEvent as ai, SandboxProviderBase as aj, SandboxProviderCatalogEntry as ak, SandboxProviderConfig as al, SaveAgentRequest as am, SaveAgentResult as an, SearchAgentSelectorParams as ao, SearchAgentsParams as ap, SelectRegistrySkillRequest as aq, SessionEventItem as ar, SkillBase as as, SkillCatalogEntry as at, SkillCatalogServer as au, SkillConfigBase as av, SkillMount as aw, SkillSelectorEntry as ax, ToolApprovalRequiredEvent as ay, ToolBase as az, ThreadCreatedEvent as b, TurnStateCancelled as b0, TurnStateError as b1, TurnStateRunning as b2, UserMessageContent as b3, UserToolApprovalEvent as c, Turn as d, TurnInputItem as e, AgentBuilderCapabilitiesResponse as f, AgentBuilderServer as g, AgentCapabilityConfig as h, AgentLibraryEntry as i, AgentRuntimeConfig as j, AgentSelectorEntry as k, AgentSkill as l, AgentUIServer as m, AgentUIServerPort as n, ApprovalDecision as o, AuthenticateConnectorRequest as p, ConnectorAuth as q, ConnectorAuthApiKey as r, ConnectorAuthNone as s, ConnectorAuthOAuth as t, ConnectorAuthPublic as u, ConnectorAuthPublicApiKey as v, ConnectorAuthPublicNone as w, ConnectorAuthPublicOAuth as x, ConnectorAuthType as y, ConnectorAuthenticationResult as z };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@truefoundry/assistant-ui-runtime",
3
- "version": "0.1.13",
3
+ "version": "0.1.14",
4
4
  "description": "TrueFoundry Gateway agent runtime adapter for assistant-ui",
5
5
  "license": "Apache-2.0",
6
6
  "repository": {
@@ -150,4 +150,111 @@ describe("useDraftAgentSpec", () => {
150
150
  expect(syncAgentSpec).toHaveBeenCalledOnce();
151
151
  expect(result.current.isSpecSyncing).toBe(false);
152
152
  });
153
+
154
+ it("flushes pending work before a coordinated save", async () => {
155
+ const syncAgentSpec = vi
156
+ .fn()
157
+ .mockResolvedValue("2026-06-30T15:00:00.000Z");
158
+ const draftBridge: DraftSessionBridge = {
159
+ getDraftAgentSpec: vi.fn().mockResolvedValue(defaultAgentSpec),
160
+ syncAgentSpec,
161
+ };
162
+ const { result } = renderHook(() =>
163
+ useDraftAgentSpec({
164
+ draftSessionId: "draft-1",
165
+ draftBridge,
166
+ defaultAgentSpec,
167
+ }),
168
+ );
169
+ await flushMicrotasks();
170
+
171
+ act(() => {
172
+ result.current.updateAgentSpec({ instructions: "latest draft" });
173
+ });
174
+ await act(async () => {
175
+ await result.current.flushAgentSpec();
176
+ });
177
+
178
+ expect(syncAgentSpec).toHaveBeenCalledWith("draft-1", {
179
+ model: defaultAgentSpec.model,
180
+ instructions: "latest draft",
181
+ });
182
+ });
183
+
184
+ it("adopts an atomically persisted spec without scheduling another sync", async () => {
185
+ const syncAgentSpec = vi.fn().mockResolvedValue("unused");
186
+ const draftBridge: DraftSessionBridge = {
187
+ getDraftAgentSpec: vi.fn().mockResolvedValue(defaultAgentSpec),
188
+ syncAgentSpec,
189
+ };
190
+ const { result } = renderHook(() =>
191
+ useDraftAgentSpec({
192
+ draftSessionId: "draft-1",
193
+ draftBridge,
194
+ defaultAgentSpec,
195
+ }),
196
+ );
197
+ await flushMicrotasks();
198
+
199
+ act(() => {
200
+ result.current.adoptAgentSpec({
201
+ agentSpec: {
202
+ model: { name: "openai/gpt-5" },
203
+ config: { generativeUi: { enabled: false } },
204
+ },
205
+ updatedAt: "2026-06-30T16:00:00.000Z",
206
+ });
207
+ });
208
+ await act(async () => {
209
+ await vi.advanceTimersByTimeAsync(400);
210
+ });
211
+
212
+ expect(result.current.agentSpec).toEqual({
213
+ model: { name: "openai/gpt-5" },
214
+ config: { generativeUi: { enabled: false } },
215
+ });
216
+ expect(syncAgentSpec).not.toHaveBeenCalled();
217
+ await expect(result.current.takeTurnHeaderTimestamp()).resolves.toBe(
218
+ "2026-06-30T16:00:00.000Z",
219
+ );
220
+ });
221
+
222
+ it("preserves the last sync timestamp when adopt omits updatedAt", async () => {
223
+ const syncAgentSpec = vi
224
+ .fn()
225
+ .mockResolvedValue("2026-06-30T17:00:00.000Z");
226
+ const draftBridge: DraftSessionBridge = {
227
+ getDraftAgentSpec: vi.fn().mockResolvedValue(defaultAgentSpec),
228
+ syncAgentSpec,
229
+ };
230
+ const { result } = renderHook(() =>
231
+ useDraftAgentSpec({
232
+ draftSessionId: "draft-1",
233
+ draftBridge,
234
+ defaultAgentSpec,
235
+ }),
236
+ );
237
+ await flushMicrotasks();
238
+
239
+ act(() => {
240
+ result.current.updateAgentSpec({ instructions: "synced" });
241
+ });
242
+ await act(async () => {
243
+ await vi.advanceTimersByTimeAsync(400);
244
+ });
245
+ await flushMicrotasks();
246
+ expect(syncAgentSpec).toHaveBeenCalledOnce();
247
+
248
+ act(() => {
249
+ result.current.adoptAgentSpec({
250
+ agentSpec: {
251
+ model: { name: "openai/gpt-5" },
252
+ },
253
+ });
254
+ });
255
+
256
+ await expect(result.current.takeTurnHeaderTimestamp()).resolves.toBe(
257
+ "2026-06-30T17:00:00.000Z",
258
+ );
259
+ });
153
260
  });
@@ -23,6 +23,11 @@ export type UseDraftAgentSpecResult = {
23
23
  isSpecSyncing: boolean;
24
24
  specError: unknown | null;
25
25
  updateAgentSpec: (update: AgentSpecUpdate) => void;
26
+ flushAgentSpec: () => Promise<void>;
27
+ adoptAgentSpec: (request: {
28
+ agentSpec: AgentSpec;
29
+ updatedAt?: string;
30
+ }) => void;
26
31
  takeTurnHeaderTimestamp: () => Promise<string | undefined>;
27
32
  };
28
33
 
@@ -206,6 +211,35 @@ export function useDraftAgentSpec({
206
211
  }
207
212
  }, []);
208
213
 
214
+ const adoptAgentSpec = useCallback(
215
+ ({
216
+ agentSpec: persistedSpec,
217
+ updatedAt,
218
+ }: {
219
+ agentSpec: AgentSpec;
220
+ updatedAt?: string;
221
+ }) => {
222
+ if (syncTimeoutRef.current != null) {
223
+ clearTimeout(syncTimeoutRef.current);
224
+ syncTimeoutRef.current = undefined;
225
+ }
226
+ syncGenerationRef.current++;
227
+ pendingFlushRef.current = undefined;
228
+ inFlightFlushRef.current = undefined;
229
+ localDirtyRef.current = false;
230
+ // Keep the last successful sync timestamp when the caller omits
231
+ // updatedAt (e.g. a save that did not return sessionUpdatedAt).
232
+ if (updatedAt !== undefined) {
233
+ lastUpdatedAtRef.current = updatedAt;
234
+ }
235
+ agentSpecRef.current = persistedSpec;
236
+ setAgentSpec(persistedSpec);
237
+ setSpecError(null);
238
+ setIsSpecSyncing(false);
239
+ },
240
+ [],
241
+ );
242
+
209
243
  const takeTurnHeaderTimestamp = useCallback(async () => {
210
244
  await flushPendingSpecSyncNow();
211
245
  const updatedAt = lastUpdatedAtRef.current;
@@ -245,6 +279,8 @@ export function useDraftAgentSpec({
245
279
  isSpecSyncing: enabled ? isSpecSyncing : false,
246
280
  specError: enabled ? specError : null,
247
281
  updateAgentSpec,
282
+ flushAgentSpec: flushPendingSpecSyncNow,
283
+ adoptAgentSpec,
248
284
  takeTurnHeaderTimestamp,
249
285
  }),
250
286
  [
@@ -254,6 +290,8 @@ export function useDraftAgentSpec({
254
290
  isSpecLoading,
255
291
  isSpecSyncing,
256
292
  specError,
293
+ flushPendingSpecSyncNow,
294
+ adoptAgentSpec,
257
295
  takeTurnHeaderTimestamp,
258
296
  updateAgentSpec,
259
297
  ],
package/src/hooks.ts CHANGED
@@ -142,6 +142,13 @@ export const useTrueFoundryHistoryPagination = () => {
142
142
  );
143
143
  };
144
144
 
145
+ /**
146
+ * True while a turn runs that this server cannot stream, so its result will not
147
+ * arrive in this client. Falls back to `false` on runtimes that predate the flag.
148
+ */
149
+ export const useTrueFoundryResumeUnavailable = () =>
150
+ trueFoundryExtras.use((e) => e.resumeUnavailable, false) ?? false;
151
+
145
152
  /** Returns a function to reset (re-submit) a user turn from any render context. */
146
153
  export const useTrueFoundryResetFromTurn = () => {
147
154
  const aui = useAui();
@@ -165,3 +172,18 @@ export const useTrueFoundryUpdateAgentSpec = () => {
165
172
  trueFoundryExtras.get(aui).draft?.updateAgentSpec(update);
166
173
  };
167
174
 
175
+ /** Flushes any pending draft-spec synchronization before a coordinated write. */
176
+ export const useTrueFoundryFlushAgentSpec = () => {
177
+ const aui = useAui();
178
+ return () =>
179
+ trueFoundryExtras.get(aui).draft?.flushAgentSpec() ?? Promise.resolve();
180
+ };
181
+
182
+ /** Adopts a spec already persisted by another server operation without syncing again. */
183
+ export const useTrueFoundryAdoptAgentSpec = () => {
184
+ const aui = useAui();
185
+ return (
186
+ request: Parameters<TrueFoundryDraftRuntimeExtras["adoptAgentSpec"]>[0],
187
+ ) => trueFoundryExtras.get(aui).draft?.adoptAgentSpec(request);
188
+ };
189
+
package/src/index.ts CHANGED
@@ -25,7 +25,9 @@ export { createTrueFoundryOwnedSessionsThreadListAdapter } from "./truefoundryOw
25
25
  export { createDraftSessionBridge } from "./draft/draftSessionBridge.js";
26
26
  export type { DraftSessionBridge } from "./draft/draftSessionBridge.js";
27
27
  export {
28
+ useTrueFoundryAdoptAgentSpec,
28
29
  useTrueFoundryAgentSpec,
30
+ useTrueFoundryFlushAgentSpec,
29
31
  useTrueFoundryUpdateAgentSpec,
30
32
  } from "./hooks.js";
31
33
  export type { TrueFoundryDraftRuntimeExtras } from "./truefoundryExtras.js";
@@ -55,6 +57,7 @@ export {
55
57
  useTrueFoundryResetFromTurn,
56
58
  useTrueFoundryReload,
57
59
  useTrueFoundryHistoryPagination,
60
+ useTrueFoundryResumeUnavailable,
58
61
  } from "./hooks.js";
59
62
  export {
60
63
  collectApprovalInputs,
@@ -79,7 +82,10 @@ export type {
79
82
  AgentChatServer,
80
83
  AgentBuilderCapabilitiesResponse,
81
84
  AgentBuilderServer,
85
+ AgentCapabilityConfig,
86
+ AgentRuntimeConfig,
82
87
  SaveAgentRequest,
88
+ SaveAgentResult,
83
89
  Session,
84
90
  Turn,
85
91
  TurnState,
@@ -9,6 +9,7 @@ import type {
9
9
  AgentSelectorEntry,
10
10
  ConnectorSelectorEntry,
11
11
  ModelSelectorEntry,
12
+ SaveAgentRequest,
12
13
  SearchAgentSelectorParams,
13
14
  SkillSelectorEntry,
14
15
  } from "../../server/types.js";
@@ -595,7 +596,7 @@ export function buildSaveAgentManifest(
595
596
  */
596
597
  export async function saveAgent(
597
598
  opts: CpCredentials,
598
- req: { agentName: string; agentSpec: TfyAgentSpec },
599
+ req: SaveAgentRequest<TfyAgentSpec>,
599
600
  ): Promise<unknown> {
600
601
  const manifest = buildSaveAgentManifest(req.agentName, req.agentSpec);
601
602
  return cpFetch(opts, "/api/svc/v1/agents", {
@@ -67,9 +67,9 @@ export interface TfyAgentSpec
67
67
  extends AgentSpec<
68
68
  TruefoundryGatewayApi.Model,
69
69
  TfySkillMount,
70
- TfyMcpServerMount
70
+ TfyMcpServerMount,
71
+ TruefoundryGatewayApi.RuntimeConfig
71
72
  > {
72
- config?: TruefoundryGatewayApi.RuntimeConfig;
73
73
  responseFormat?: TruefoundryGatewayApi.ResponseFormat;
74
74
  messages?: TruefoundryGatewayApi.AgentSpecUserMessage[];
75
75
  }
@@ -15,8 +15,11 @@ export type {
15
15
  McpServerMount,
16
16
  ModelParams,
17
17
  Model,
18
+ AgentCapabilityConfig,
19
+ AgentRuntimeConfig,
18
20
  AgentSpec,
19
21
  SaveAgentRequest,
22
+ SaveAgentResult,
20
23
  Session,
21
24
  CreateSessionRequest,
22
25
  UpdateSessionRequest,
@@ -105,19 +105,31 @@ export interface Model {
105
105
  params?: ModelParams;
106
106
  }
107
107
 
108
+ export interface AgentCapabilityConfig {
109
+ enabled?: boolean;
110
+ }
111
+
112
+ export interface AgentRuntimeConfig {
113
+ generativeUi?: AgentCapabilityConfig;
114
+ dynamicSubAgents?: AgentCapabilityConfig;
115
+ askUserQuestions?: AgentCapabilityConfig;
116
+ }
117
+
108
118
  /**
109
119
  * SDK-owned agent definition — fields the FE reads/writes.
110
- * Host widens `model` / `skills` / `mcpServers` via type params, and adds
111
- * extra fields via `TSpec extends AgentSpec<...>`.
120
+ * Host widens `model` / `skills` / `mcpServers` / `config` via type params,
121
+ * and adds extra fields via `TSpec extends AgentSpec<...>`.
112
122
  */
113
123
  export interface AgentSpec<
114
124
  TModel extends Model = Model,
115
125
  TSkill extends SkillMount = SkillMount,
116
126
  TMcp extends McpServerMount = McpServerMount,
127
+ TConfig extends AgentRuntimeConfig = AgentRuntimeConfig,
117
128
  > {
118
129
  model: TModel;
119
130
  skills?: TSkill[];
120
131
  mcpServers?: TMcp[];
132
+ config?: TConfig;
121
133
  instructions?: string;
122
134
  variables?: Record<string, string>;
123
135
  }
@@ -329,6 +341,16 @@ export interface AgentChatServer<
329
341
  export interface SaveAgentRequest<TSpec extends AgentSpec = AgentSpec> {
330
342
  agentName: string;
331
343
  agentSpec: TSpec;
344
+ intent: "create" | "update";
345
+ /** Current mutable session to update atomically with the named agent. */
346
+ sessionId?: string;
347
+ }
348
+
349
+ export interface SaveAgentResult {
350
+ /** Immutable id allocated by the host registry. */
351
+ agentId?: string;
352
+ /** Timestamp returned when the active mutable session was updated. */
353
+ sessionUpdatedAt?: string;
332
354
  }
333
355
 
334
356
  /**
@@ -352,7 +374,7 @@ export interface AgentBuilderServer<
352
374
  TSkill extends SkillSelectorEntry = SkillSelectorEntry,
353
375
  TMcp extends ConnectorSelectorEntry = ConnectorSelectorEntry,
354
376
  TAgent extends AgentSelectorEntry = AgentSelectorEntry,
355
- TSave = unknown,
377
+ TSave = SaveAgentResult,
356
378
  TCapabilities extends AgentBuilderCapabilitiesResponse = AgentBuilderCapabilitiesResponse,
357
379
  > {
358
380
  getCapabilities(): Promise<TCapabilities>;
@@ -357,6 +357,24 @@ describe("streamTurn", () => {
357
357
  expect(updates).toEqual([{ content: [{ type: "text", text: "resumed" }] }]);
358
358
  });
359
359
 
360
+ it("yields nothing when the server omits subscribeToTurn", async () => {
361
+ const cancelSession = vi.fn().mockResolvedValue(undefined);
362
+ const server = mockServer({ cancelSession });
363
+
364
+ const updates = await collectUpdates(
365
+ resumeTurnStream(
366
+ server,
367
+ SESSION_ID,
368
+ "turn-1",
369
+ new PeerThreadFoldState(),
370
+ new AbortController().signal,
371
+ ),
372
+ );
373
+
374
+ expect(updates).toEqual([]);
375
+ expect(cancelSession).not.toHaveBeenCalled();
376
+ });
377
+
360
378
  it("returns early when aborted before streaming starts", async () => {
361
379
  const subscribeToTurn = vi.fn(async function* () {});
362
380
  const cancelSession = vi.fn().mockResolvedValue(undefined);
package/src/streamTurn.ts CHANGED
@@ -116,10 +116,10 @@ export async function* resumeTurnStream(
116
116
  afterSequenceNumber?: number,
117
117
  groupRootBaseline?: readonly string[],
118
118
  ): AsyncGenerator<TurnStreamUpdate> {
119
+ // Optional on custom backends. Callers detect the gap and report it, so an
120
+ // empty stream here is safer than throwing mid-render.
119
121
  if (server.subscribeToTurn == null) {
120
- throw new Error(
121
- "resumeTurnStream requires AgentChatServer.subscribeToTurn",
122
- );
122
+ return;
123
123
  }
124
124
 
125
125
  const onAbort = () => {
@@ -16,12 +16,18 @@ export type TrueFoundryDraftRuntimeExtras = {
16
16
  isSpecSyncing: boolean;
17
17
  specError: unknown | null;
18
18
  updateAgentSpec: (update: AgentSpecUpdate) => void;
19
+ flushAgentSpec: () => Promise<void>;
20
+ adoptAgentSpec: (request: {
21
+ agentSpec: AgentSpec;
22
+ updatedAt?: string;
23
+ }) => void;
19
24
  };
20
25
 
21
26
  export type TrueFoundryRuntimeExtras = {
22
27
  pendingApprovals: PendingApproval[];
23
28
  pendingToolResponses: PendingToolResponse[];
24
29
  pendingMcpAuth: { mcpServers: McpAuthRequiredEvent["mcpServers"] } | null;
30
+ resumeUnavailable: boolean;
25
31
  sandboxId: string | undefined;
26
32
  respondToToolApproval: (response: RespondToToolApprovalOptions) => void;
27
33
  respondToToolResponse: (response: RespondToToolResponseOptions) => void;
@@ -49,4 +55,10 @@ export const EMPTY_DRAFT_EXTRAS: TrueFoundryDraftRuntimeExtras = {
49
55
  updateAgentSpec: () => {
50
56
  throw new Error("Draft agent extras are only available in draft mode.");
51
57
  },
58
+ flushAgentSpec: async () => {
59
+ throw new Error("Draft agent extras are only available in draft mode.");
60
+ },
61
+ adoptAgentSpec: () => {
62
+ throw new Error("Draft agent extras are only available in draft mode.");
63
+ },
52
64
  };