@truefoundry/assistant-ui-runtime 0.1.8 → 0.1.10-rc.1

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, 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';
3
+ import { A as AgentSpec, x as CreateSessionRequest, B as ListSessionsParams, S as Session, d as Turn, a7 as TurnState, a as AgentChatServer, ae as UpdateSessionRequest, ag as AgentSelectorEntry, ah as ConnectorSelectorEntry, ai as ModelSelectorEntry, aj as SkillSelectorEntry, f as AgentBuilderServer } from '../../types-CgZUnILu.js';
4
4
  import { TruefoundryGatewayApi } from 'truefoundry-gateway-sdk';
5
5
 
6
6
  /**
@@ -7,7 +7,7 @@ import {
7
7
  isTfyMcpToolInfo,
8
8
  isTfySystemToolInfo,
9
9
  isTfyToolInfo
10
- } from "../../chunk-CXBZ6WLZ.js";
10
+ } from "../../chunk-BRPTG6VA.js";
11
11
  export {
12
12
  createTrueFoundryAgentUIServer,
13
13
  createTrueFoundryChatServer,
@@ -1,5 +1,5 @@
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';
1
+ import { aa as TurnStreamingEvent, ak as DeltaEvents, T as TurnEvent } from '../types-CgZUnILu.js';
2
+ export { al as ActionRequiredEvent, f as AgentBuilderServer, a as AgentChatServer, am as AgentInfo, an as AgentParent, ag as AgentSelectorEntry, A as AgentSpec, g as AgentUIServerPort, ao as ApprovalDecision, C as CatalogServer, ap 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, ah as ConnectorSelectorEntry, u as CreateConnectorRequest, v as CreateModelProviderRequest, w as CreateSandboxRequest, x as CreateSessionRequest, y as CreateSkillRequest, z as CreateSkillRequestBase, D as DefinedSkill, G as GithubSkill, I as ImportGithubSkillRequest, L as ListResult, aq as ListSessionsOrder, B as ListSessionsParams, M as McpAuthRequiredEvent, ar as McpInitializeEvent, as as McpServerAuthInfo, at as McpServerMount, au as Model, E as ModelCatalogServer, F as ModelEntry, av as ModelMessageContentPart, aw as ModelMessageDeltaEvent, H as ModelMessageEvent, ax as ModelParams, J as ModelProviderBase, K as ModelProviderCatalogEntry, N as ModelProviderConfigBase, ai as ModelSelectorEntry, ay as PageParams, P as PreviousTurnIdInput, O as ProviderType, R as RegistrySkill, Q as SandboxBase, V as SandboxCatalogEntry, W as SandboxCatalogServer, X as SandboxConfig, Y as SandboxCreatedEvent, az as SearchAgentSelectorParams, Z as SelectRegistrySkillRequest, S as Session, _ as SessionEventItem, $ as SkillBase, a0 as SkillCatalogEntry, a1 as SkillCatalogServer, a2 as SkillConfigBase, aA as SkillMount, aj as SkillSelectorEntry, b as ThreadCreatedEvent, aB as ThreadDoneEvent, a3 as ToolApprovalRequiredEvent, a4 as ToolBase, a5 as ToolCall, aC as ToolCallFunction, aD as ToolCallRef, aE as ToolInfo, aF as ToolResponseEvent, a6 as ToolResponseRequiredEvent, d as Turn, aG as TurnCreatedEvent, aH as TurnDoneEvent, e as TurnInputItem, a7 as TurnState, aI as TurnStateCancelled, a8 as TurnStateDone, aJ as TurnStateError, aK as TurnStateRunning, a9 as TurnStreamData, ab as UpdateConnectorRequest, ac as UpdateModelProviderRequest, ad as UpdateSandboxRequest, ae as UpdateSessionRequest, af as UserMessage, aL as UserMessageContent, c as UserToolApprovalEvent, U as UserToolResponseEvent } from '../types-CgZUnILu.js';
3
3
 
4
4
  /**
5
5
  * Local implementations of streaming delta helpers.
@@ -231,6 +231,7 @@ type DeltaEvents = ModelMessageDeltaEvent;
231
231
  interface ModelSelectorEntry {
232
232
  name: string;
233
233
  provider: string;
234
+ reasoningEfforts?: string[];
234
235
  }
235
236
  /** Skill selector row. Host extends for fqn, preload, etc. */
236
237
  interface SkillSelectorEntry {
@@ -441,7 +442,15 @@ interface AgentChatServer<TSpec extends AgentSpec = AgentSpec, TSession extends
441
442
  afterSequenceNumber?: number;
442
443
  abortSignal?: AbortSignal;
443
444
  }): AsyncIterable<TurnStreamData>;
444
- downloadSandboxFile?(sandboxId: string, req: {
445
+ /**
446
+ * Reads a file the agent wrote inside its sandbox. Hosts whose download route is scoped to a
447
+ * turn resolve the sandbox from `turnId` and ignore `sandboxId`; hosts addressing sandboxes
448
+ * directly use `sandboxId`.
449
+ */
450
+ downloadSandboxFile?(req: {
451
+ sessionId: string;
452
+ turnId: string;
453
+ sandboxId: string;
445
454
  path: string;
446
455
  }): Promise<Blob>;
447
456
  }
@@ -532,15 +541,16 @@ interface ModelCatalogServer<TModel extends ModelEntry = ModelEntry, TProvider e
532
541
  interface ToolBase {
533
542
  id: string;
534
543
  name: string;
544
+ description: string;
535
545
  }
536
546
  /** Strict auth type id. Hosts widen branches via intersection + re-union. */
537
- type ConnectorAuthType = "oauth" | "apiKey" | "none";
547
+ type ConnectorAuthType = "dcr" | "header" | "none";
538
548
  type ConnectorAuthOAuth = {
539
- type: "oauth";
549
+ type: "dcr";
540
550
  authUrl?: string;
541
551
  };
542
552
  type ConnectorAuthApiKey = {
543
- type: "apiKey";
553
+ type: "header";
544
554
  apiKey?: string;
545
555
  headerName?: string;
546
556
  };
@@ -549,11 +559,11 @@ type ConnectorAuthNone = {
549
559
  };
550
560
  type ConnectorAuth = ConnectorAuthOAuth | ConnectorAuthApiKey | ConnectorAuthNone;
551
561
  type ConnectorAuthPublicOAuth = {
552
- type: "oauth";
562
+ type: "dcr";
553
563
  authUrl: string;
554
564
  };
555
565
  type ConnectorAuthPublicApiKey = {
556
- type: "apiKey";
566
+ type: "header";
557
567
  headerName?: string;
558
568
  };
559
569
  type ConnectorAuthPublicNone = {
@@ -626,12 +636,36 @@ interface SkillBase {
626
636
  name: string;
627
637
  description: string;
628
638
  }
629
- /** Create-skill request. Host extends for branch, auth, etc. */
630
- interface CreateSkillRequest {
631
- repo: string;
632
- directory: string;
639
+ interface RegistrySkill extends SkillBase {
640
+ /** `SkillCatalogEntry.id` this skill was created from. */
641
+ catalogId: string;
642
+ }
643
+ interface GithubSkill extends SkillBase {
644
+ }
645
+ type DefinedSkill = RegistrySkill | GithubSkill;
646
+ /** Git source fields shared by catalog entries and create requests. */
647
+ interface SkillConfigBase {
648
+ name: string;
649
+ description: string;
650
+ repoURL: string;
651
+ path: string;
652
+ ref: string;
633
653
  }
634
- interface SkillCatalogServer<TSkill extends SkillBase = SkillBase, TCreate extends CreateSkillRequest = CreateSkillRequest> {
654
+ interface SkillCatalogEntry extends SkillConfigBase {
655
+ id: string;
656
+ }
657
+ /** Create-skill base. Hosts may intersect extra fields and re-union. */
658
+ interface CreateSkillRequestBase extends SkillConfigBase {
659
+ }
660
+ interface SelectRegistrySkillRequest extends CreateSkillRequestBase {
661
+ /** `SkillCatalogEntry.id`, persisted so the created skill links back to it. */
662
+ catalogId: string;
663
+ }
664
+ interface ImportGithubSkillRequest extends CreateSkillRequestBase {
665
+ }
666
+ type CreateSkillRequest = SelectRegistrySkillRequest | ImportGithubSkillRequest;
667
+ interface SkillCatalogServer<TSkill extends SkillBase = SkillBase, TCatalogEntry extends SkillCatalogEntry = SkillCatalogEntry, TCreate extends CreateSkillRequest = CreateSkillRequest> {
668
+ getSkillCatalog(): Promise<TCatalogEntry[]>;
635
669
  listSkills(req?: {
636
670
  query?: string;
637
671
  }): Promise<TSkill[]>;
@@ -640,15 +674,59 @@ interface SkillCatalogServer<TSkill extends SkillBase = SkillBase, TCreate exten
640
674
  id: string;
641
675
  }): Promise<void>;
642
676
  }
677
+ /** Mutable sandbox settings shared by catalog rows, create, and update. */
678
+ interface SandboxConfig {
679
+ snapshotName: string;
680
+ execTimeoutMs: number;
681
+ autoStopIntervalInMinutes: number;
682
+ autoArchiveIntervalInMinutes: number;
683
+ autoDeleteIntervalInMinutes: number;
684
+ }
685
+ interface SandboxCatalogEntry extends SandboxConfig {
686
+ id: string;
687
+ name: string;
688
+ type: string;
689
+ }
690
+ interface SandboxBase {
691
+ id: string;
692
+ name: string;
693
+ catalogId: string;
694
+ isConnected: boolean;
695
+ }
696
+ interface CreateSandboxRequest extends SandboxConfig {
697
+ /** `SandboxCatalogEntry.id` used to create this sandbox. */
698
+ catalogId: string;
699
+ name: string;
700
+ type: string;
701
+ apiKey: string;
702
+ }
703
+ interface UpdateSandboxRequest extends SandboxConfig {
704
+ id: string;
705
+ apiKey: string;
706
+ }
707
+ interface SandboxCatalogServer<TSandbox extends SandboxBase = SandboxBase, TCatalogEntry extends SandboxCatalogEntry = SandboxCatalogEntry, TCreate extends CreateSandboxRequest = CreateSandboxRequest, TUpdate extends UpdateSandboxRequest = UpdateSandboxRequest> {
708
+ getSandboxCatalog(): Promise<TCatalogEntry[]>;
709
+ listSandboxes(req?: {
710
+ query?: string;
711
+ }): Promise<TSandbox[]>;
712
+ createSandbox(req: TCreate): Promise<TSandbox>;
713
+ updateSandbox(req: TUpdate): Promise<TSandbox>;
714
+ deleteSandbox(req: {
715
+ id: string;
716
+ }): Promise<void>;
717
+ }
643
718
  /**
644
- * Settings management aggregate — modelCatalog + connectorCatalog + optional skillCatalog.
719
+ * Settings management aggregate — modelCatalog + connectorCatalog + optional
720
+ * skill and sandbox catalogs.
645
721
  * Hosts may pass the whole object to an app shell, or a focused sub-port to a page.
646
722
  */
647
- interface CatalogServer<TModelCatalog extends ModelCatalogServer = ModelCatalogServer, TConnectorCatalog extends ConnectorCatalogServer = ConnectorCatalogServer, TSkillCatalog extends SkillCatalogServer = SkillCatalogServer> {
723
+ interface CatalogServer<TModelCatalog extends ModelCatalogServer = ModelCatalogServer, TConnectorCatalog extends ConnectorCatalogServer = ConnectorCatalogServer, TSkillCatalog extends SkillCatalogServer = SkillCatalogServer, TSandboxCatalog extends SandboxCatalogServer = SandboxCatalogServer> {
648
724
  modelCatalog: TModelCatalog;
649
725
  connectorCatalog: TConnectorCatalog;
650
726
  /** Optional — omit when the host has no skills settings surface. */
651
727
  skillCatalog?: TSkillCatalog;
728
+ /** Optional — omit when the host has no sandboxes settings surface. */
729
+ sandboxCatalog?: TSandboxCatalog;
652
730
  }
653
731
  /**
654
732
  * Composed host port: chat + builder + optional settings catalog.
@@ -663,4 +741,4 @@ type AgentUIServerPort<TChat extends AgentChatServer = AgentChatServer, TBuilder
663
741
  catalog?: TCatalog;
664
742
  };
665
743
 
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 };
744
+ export type { SkillBase as $, AgentSpec as A, ListSessionsParams as B, CatalogServer as C, DefinedSkill as D, ModelCatalogServer as E, ModelEntry as F, GithubSkill as G, ModelMessageEvent as H, ImportGithubSkillRequest as I, ModelProviderBase as J, ModelProviderCatalogEntry as K, ListResult as L, McpAuthRequiredEvent as M, ModelProviderConfigBase as N, ProviderType as O, PreviousTurnIdInput as P, SandboxBase as Q, RegistrySkill as R, Session as S, TurnEvent as T, UserToolResponseEvent as U, SandboxCatalogEntry as V, SandboxCatalogServer as W, SandboxConfig as X, SandboxCreatedEvent as Y, SelectRegistrySkillRequest as Z, SessionEventItem as _, AgentChatServer as a, SkillCatalogEntry as a0, SkillCatalogServer as a1, SkillConfigBase as a2, ToolApprovalRequiredEvent as a3, ToolBase as a4, ToolCall as a5, ToolResponseRequiredEvent as a6, TurnState as a7, TurnStateDone as a8, TurnStreamData as a9, SkillMount as aA, ThreadDoneEvent as aB, ToolCallFunction as aC, ToolCallRef as aD, ToolInfo as aE, ToolResponseEvent as aF, TurnCreatedEvent as aG, TurnDoneEvent as aH, TurnStateCancelled as aI, TurnStateError as aJ, TurnStateRunning as aK, UserMessageContent as aL, TurnStreamingEvent as aa, UpdateConnectorRequest as ab, UpdateModelProviderRequest as ac, UpdateSandboxRequest as ad, UpdateSessionRequest as ae, UserMessage as af, AgentSelectorEntry as ag, ConnectorSelectorEntry as ah, ModelSelectorEntry as ai, SkillSelectorEntry as aj, DeltaEvents as ak, ActionRequiredEvent as al, AgentInfo as am, AgentParent as an, ApprovalDecision as ao, ChunkDeltaToolCall as ap, ListSessionsOrder as aq, McpInitializeEvent as ar, McpServerAuthInfo as as, McpServerMount as at, Model as au, ModelMessageContentPart as av, ModelMessageDeltaEvent as aw, ModelParams as ax, PageParams as ay, SearchAgentSelectorParams as az, 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, CreateSandboxRequest as w, CreateSessionRequest as x, CreateSkillRequest as y, CreateSkillRequestBase as z };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@truefoundry/assistant-ui-runtime",
3
- "version": "0.1.8",
3
+ "version": "0.1.10-rc.1",
4
4
  "description": "TrueFoundry Gateway agent runtime adapter for assistant-ui",
5
5
  "license": "Apache-2.0",
6
6
  "repository": {
@@ -461,6 +461,26 @@ describe("convertTurnMessages", () => {
461
461
  expect(updated.id).toBe(existing.id);
462
462
  expect(updated.content).toEqual([{ type: "text", text: "second" }]);
463
463
  });
464
+
465
+ it("stamps the streaming turn id onto the message metadata", () => {
466
+ const message = turnStreamUpdateToAssistantMessage("turn-1", {
467
+ content: [{ type: "text", text: "hi" }],
468
+ });
469
+ expect(message.metadata.custom.turnId).toBe("turn-1");
470
+ });
471
+
472
+ it("advances the turn id when a continuation reuses the existing message", () => {
473
+ const existing = turnStreamUpdateToAssistantMessage("turn-1", {
474
+ content: [{ type: "text", text: "first" }],
475
+ });
476
+ const updated = turnStreamUpdateToAssistantMessage(
477
+ "turn-2",
478
+ { content: [{ type: "text", text: "second" }] },
479
+ existing,
480
+ );
481
+ expect(updated.id).toBe("turn-1-assistant");
482
+ expect(updated.metadata.custom.turnId).toBe("turn-2");
483
+ });
464
484
  });
465
485
 
466
486
  describe("repositoryItemsFromMessages", () => {
@@ -546,7 +566,7 @@ describe("convertTurnMessages", () => {
546
566
 
547
567
  expect(result.messages[1]).toMatchObject({
548
568
  role: "assistant",
549
- metadata: { custom: { sandboxId: "sbx-123" } },
569
+ metadata: { custom: { sandboxId: "sbx-123", turnId: "turn-1" } },
550
570
  });
551
571
  });
552
572
 
@@ -601,6 +621,8 @@ describe("convertTurnMessages", () => {
601
621
  { type: "text", text: "first chunk" },
602
622
  { type: "text", text: " after approval" },
603
623
  ]);
624
+ // Artifacts on a merged message belong to the turn that wrote them last.
625
+ expect(assistant.metadata.custom.turnId).toBe("turn-2");
604
626
  });
605
627
 
606
628
  it("preserves requires-action on reload when a turn ends awaiting tool approval", async () => {
@@ -1074,8 +1074,14 @@ function projectHistoryTurns(
1074
1074
  content,
1075
1075
  record,
1076
1076
  );
1077
- const custom =
1078
- record.sandboxId != null ? { ...baseCustom, sandboxId: record.sandboxId } : baseCustom;
1077
+ // Continuation turns fold into the previous assistant message and overwrite this custom,
1078
+ // so a folded message reports the latest turn that contributed to it — the one whose
1079
+ // sandbox wrote the newest artifacts.
1080
+ const custom = {
1081
+ ...baseCustom,
1082
+ turnId: record.id,
1083
+ ...(record.sandboxId != null ? { sandboxId: record.sandboxId } : {}),
1084
+ };
1079
1085
 
1080
1086
  if (record.userText) {
1081
1087
  messages.push(
@@ -1732,7 +1738,7 @@ export function turnStreamUpdateToAssistantMessage(
1732
1738
  unstable_annotations: [],
1733
1739
  unstable_data: [],
1734
1740
  steps: [],
1735
- custom: update.metadata?.custom ?? {},
1741
+ custom: { ...update.metadata?.custom, turnId },
1736
1742
  },
1737
1743
  };
1738
1744
  }
package/src/hooks.ts CHANGED
@@ -1,8 +1,9 @@
1
1
  "use client";
2
2
 
3
3
  import { useMemo } from "react";
4
- import { useAui } from "@assistant-ui/store";
4
+ import { useAui, useAuiState } from "@assistant-ui/store";
5
5
 
6
+ import type { TrueFoundryMessageCustomMetadata } from "./messageCustomMetadata.js";
6
7
  import {
7
8
  EMPTY_DRAFT_EXTRAS,
8
9
  trueFoundryExtras,
@@ -86,10 +87,29 @@ export const useTrueFoundryResumeMcpAuth = () => {
86
87
  export const useTrueFoundrySandboxId = (): string | undefined =>
87
88
  trueFoundryExtras.use((e) => e.sandboxId, undefined);
88
89
 
89
- /** Returns a function to download a sandbox file by path from any render context. */
90
+ /** Turn that produced the message being rendered. Only defined inside a message scope. */
91
+ export const useTrueFoundryTurnId = (): string | undefined =>
92
+ useAuiState(
93
+ (state) =>
94
+ (state.message.metadata?.custom as TrueFoundryMessageCustomMetadata | undefined)
95
+ ?.turnId,
96
+ );
97
+
98
+ /**
99
+ * Returns a function to download a file the current turn wrote to its sandbox. Must be called
100
+ * from a message scope, since the artifact belongs to the turn that rendered it.
101
+ */
90
102
  export const useTrueFoundryDownloadSandboxFile = () => {
91
103
  const aui = useAui();
92
- return (path: string) => trueFoundryExtras.get(aui).downloadSandboxFile(path);
104
+ const turnId = useTrueFoundryTurnId();
105
+ return (path: string) => {
106
+ if (turnId == null) {
107
+ throw new Error(
108
+ "Downloading a sandbox file requires a message scope to resolve its turn.",
109
+ );
110
+ }
111
+ return trueFoundryExtras.get(aui).downloadSandboxFile({ turnId, path });
112
+ };
93
113
  };
94
114
 
95
115
  /** Returns a function to cancel the current run from any render context. */
package/src/index.ts CHANGED
@@ -49,6 +49,7 @@ export {
49
49
  useTrueFoundryRespondToToolResponse,
50
50
  useTrueFoundryResumeMcpAuth,
51
51
  useTrueFoundrySandboxId,
52
+ useTrueFoundryTurnId,
52
53
  useTrueFoundryDownloadSandboxFile,
53
54
  useTrueFoundryCancel,
54
55
  useTrueFoundryResetFromTurn,
@@ -116,8 +117,22 @@ export type {
116
117
  UpdateConnectorRequest,
117
118
  ConnectorCatalogServer,
118
119
  SkillBase,
120
+ RegistrySkill,
121
+ GithubSkill,
122
+ DefinedSkill,
123
+ SkillConfigBase,
124
+ SkillCatalogEntry,
125
+ CreateSkillRequestBase,
126
+ SelectRegistrySkillRequest,
127
+ ImportGithubSkillRequest,
119
128
  CreateSkillRequest,
120
129
  SkillCatalogServer,
130
+ SandboxConfig,
131
+ SandboxCatalogEntry,
132
+ SandboxBase,
133
+ CreateSandboxRequest,
134
+ UpdateSandboxRequest,
135
+ SandboxCatalogServer,
121
136
  CatalogServer,
122
137
  AgentUIServerPort,
123
138
  } from "./server/index.js";
@@ -10,6 +10,8 @@ export type TrueFoundryMessageCustomMetadata = {
10
10
  pendingMcpAuth?: true;
11
11
  mcpServers?: McpAuthRequiredEvent["mcpServers"];
12
12
  sandboxId?: string;
13
+ /** Turn that produced this message. Scopes artifact downloads to their own turn. */
14
+ turnId?: string;
13
15
  [TOOL_APPROVAL_THREAD_ID_CUSTOM_KEY]?: string;
14
16
  [TOOL_RESPONSE_THREAD_ID_CUSTOM_KEY]?: string;
15
17
  };
@@ -350,11 +350,12 @@ export function createTrueFoundryChatServer<
350
350
  ) as AsyncIterable<TurnStreamData>;
351
351
  },
352
352
 
353
- async downloadSandboxFile(sandboxId, req) {
354
- const response = await privateClient.downloadSandboxFile(
355
- sandboxId,
356
- req,
357
- );
353
+ // The gateway addresses sandboxes directly, so the turn is carried for hosts that need it
354
+ // but is not part of this route.
355
+ async downloadSandboxFile({ sandboxId, path }) {
356
+ const response = await privateClient.downloadSandboxFile(sandboxId, {
357
+ path,
358
+ });
358
359
  return await response.blob();
359
360
  },
360
361
 
@@ -56,8 +56,22 @@ export type {
56
56
  UpdateConnectorRequest,
57
57
  ConnectorCatalogServer,
58
58
  SkillBase,
59
+ RegistrySkill,
60
+ GithubSkill,
61
+ DefinedSkill,
62
+ SkillConfigBase,
63
+ SkillCatalogEntry,
64
+ CreateSkillRequestBase,
65
+ SelectRegistrySkillRequest,
66
+ ImportGithubSkillRequest,
59
67
  CreateSkillRequest,
60
68
  SkillCatalogServer,
69
+ SandboxConfig,
70
+ SandboxCatalogEntry,
71
+ SandboxBase,
72
+ CreateSandboxRequest,
73
+ UpdateSandboxRequest,
74
+ SandboxCatalogServer,
61
75
  CatalogServer,
62
76
  AgentUIServerPort,
63
77
  } from "./types.js";
@@ -21,6 +21,7 @@ import type {
21
21
  export interface ModelSelectorEntry {
22
22
  name: string;
23
23
  provider: string;
24
+ reasoningEfforts?: string[];
24
25
  }
25
26
 
26
27
  /** Skill selector row. Host extends for fqn, preload, etc. */
@@ -290,10 +291,17 @@ export interface AgentChatServer<
290
291
  abortSignal?: AbortSignal;
291
292
  }): AsyncIterable<TurnStreamData>;
292
293
 
293
- downloadSandboxFile?(
294
- sandboxId: string,
295
- req: { path: string },
296
- ): Promise<Blob>;
294
+ /**
295
+ * Reads a file the agent wrote inside its sandbox. Hosts whose download route is scoped to a
296
+ * turn resolve the sandbox from `turnId` and ignore `sandboxId`; hosts addressing sandboxes
297
+ * directly use `sandboxId`.
298
+ */
299
+ downloadSandboxFile?(req: {
300
+ sessionId: string;
301
+ turnId: string;
302
+ sandboxId: string;
303
+ path: string;
304
+ }): Promise<Blob>;
297
305
  }
298
306
 
299
307
  /**
@@ -405,15 +413,16 @@ export interface ModelCatalogServer<
405
413
  export interface ToolBase {
406
414
  id: string;
407
415
  name: string;
416
+ description: string;
408
417
  }
409
418
 
410
419
  /** Strict auth type id. Hosts widen branches via intersection + re-union. */
411
- export type ConnectorAuthType = "oauth" | "apiKey" | "none";
420
+ export type ConnectorAuthType = "dcr" | "header" | "none";
412
421
 
413
422
  // Write (create/update) — export branches so hosts can intersect extras
414
- export type ConnectorAuthOAuth = { type: "oauth"; authUrl?: string };
423
+ export type ConnectorAuthOAuth = { type: "dcr"; authUrl?: string };
415
424
  export type ConnectorAuthApiKey = {
416
- type: "apiKey";
425
+ type: "header";
417
426
  apiKey?: string;
418
427
  headerName?: string;
419
428
  };
@@ -423,10 +432,10 @@ export type ConnectorAuth =
423
432
  | ConnectorAuthApiKey
424
433
  | ConnectorAuthNone;
425
434
 
426
- // Public (list/detail) — no secrets; oauth requires authUrl
427
- export type ConnectorAuthPublicOAuth = { type: "oauth"; authUrl: string };
435
+ // Public (list/detail) — no secrets; dcr requires authUrl
436
+ export type ConnectorAuthPublicOAuth = { type: "dcr"; authUrl: string };
428
437
  export type ConnectorAuthPublicApiKey = {
429
- type: "apiKey";
438
+ type: "header";
430
439
  headerName?: string;
431
440
  };
432
441
  export type ConnectorAuthPublicNone = { type: "none" };
@@ -525,34 +534,122 @@ export interface SkillBase {
525
534
  description: string;
526
535
  }
527
536
 
528
- /** Create-skill request. Host extends for branch, auth, etc. */
529
- export interface CreateSkillRequest {
530
- repo: string;
531
- directory: string;
537
+ export interface RegistrySkill extends SkillBase {
538
+ /** `SkillCatalogEntry.id` this skill was created from. */
539
+ catalogId: string;
540
+ }
541
+
542
+ export interface GithubSkill extends SkillBase {}
543
+
544
+ export type DefinedSkill = RegistrySkill | GithubSkill;
545
+
546
+ /** Git source fields shared by catalog entries and create requests. */
547
+ export interface SkillConfigBase {
548
+ name: string;
549
+ description: string;
550
+ repoURL: string;
551
+ path: string;
552
+ ref: string;
553
+ }
554
+
555
+ export interface SkillCatalogEntry extends SkillConfigBase {
556
+ id: string;
557
+ }
558
+
559
+ /** Create-skill base. Hosts may intersect extra fields and re-union. */
560
+ export interface CreateSkillRequestBase extends SkillConfigBase {}
561
+
562
+ export interface SelectRegistrySkillRequest extends CreateSkillRequestBase {
563
+ /** `SkillCatalogEntry.id`, persisted so the created skill links back to it. */
564
+ catalogId: string;
532
565
  }
533
566
 
567
+ export interface ImportGithubSkillRequest extends CreateSkillRequestBase {}
568
+
569
+ export type CreateSkillRequest =
570
+ | SelectRegistrySkillRequest
571
+ | ImportGithubSkillRequest;
572
+
534
573
  export interface SkillCatalogServer<
535
574
  TSkill extends SkillBase = SkillBase,
575
+ TCatalogEntry extends SkillCatalogEntry = SkillCatalogEntry,
536
576
  TCreate extends CreateSkillRequest = CreateSkillRequest,
537
577
  > {
578
+ getSkillCatalog(): Promise<TCatalogEntry[]>;
538
579
  listSkills(req?: { query?: string }): Promise<TSkill[]>;
539
580
  createSkill(req: TCreate): Promise<TSkill>;
540
581
  deleteSkill?(req: { id: string }): Promise<void>;
541
582
  }
542
583
 
584
+ // ---------------------------------------------------------------------------
585
+ // Sandboxes catalog — public rows omit credentials; writes accept them
586
+ // ---------------------------------------------------------------------------
587
+
588
+ /** Mutable sandbox settings shared by catalog rows, create, and update. */
589
+ export interface SandboxConfig {
590
+ snapshotName: string;
591
+ execTimeoutMs: number;
592
+ autoStopIntervalInMinutes: number;
593
+ autoArchiveIntervalInMinutes: number;
594
+ autoDeleteIntervalInMinutes: number;
595
+ }
596
+
597
+ export interface SandboxCatalogEntry extends SandboxConfig {
598
+ id: string;
599
+ name: string;
600
+ type: string;
601
+ }
602
+
603
+ export interface SandboxBase {
604
+ id: string;
605
+ name: string;
606
+ catalogId: string;
607
+ isConnected: boolean;
608
+ }
609
+
610
+ export interface CreateSandboxRequest extends SandboxConfig {
611
+ /** `SandboxCatalogEntry.id` used to create this sandbox. */
612
+ catalogId: string;
613
+ name: string;
614
+ type: string;
615
+ apiKey: string;
616
+ }
617
+
618
+ export interface UpdateSandboxRequest extends SandboxConfig {
619
+ id: string;
620
+ apiKey: string;
621
+ }
622
+
623
+ export interface SandboxCatalogServer<
624
+ TSandbox extends SandboxBase = SandboxBase,
625
+ TCatalogEntry extends SandboxCatalogEntry = SandboxCatalogEntry,
626
+ TCreate extends CreateSandboxRequest = CreateSandboxRequest,
627
+ TUpdate extends UpdateSandboxRequest = UpdateSandboxRequest,
628
+ > {
629
+ getSandboxCatalog(): Promise<TCatalogEntry[]>;
630
+ listSandboxes(req?: { query?: string }): Promise<TSandbox[]>;
631
+ createSandbox(req: TCreate): Promise<TSandbox>;
632
+ updateSandbox(req: TUpdate): Promise<TSandbox>;
633
+ deleteSandbox(req: { id: string }): Promise<void>;
634
+ }
635
+
543
636
  /**
544
- * Settings management aggregate — modelCatalog + connectorCatalog + optional skillCatalog.
637
+ * Settings management aggregate — modelCatalog + connectorCatalog + optional
638
+ * skill and sandbox catalogs.
545
639
  * Hosts may pass the whole object to an app shell, or a focused sub-port to a page.
546
640
  */
547
641
  export interface CatalogServer<
548
642
  TModelCatalog extends ModelCatalogServer = ModelCatalogServer,
549
643
  TConnectorCatalog extends ConnectorCatalogServer = ConnectorCatalogServer,
550
644
  TSkillCatalog extends SkillCatalogServer = SkillCatalogServer,
645
+ TSandboxCatalog extends SandboxCatalogServer = SandboxCatalogServer,
551
646
  > {
552
647
  modelCatalog: TModelCatalog;
553
648
  connectorCatalog: TConnectorCatalog;
554
649
  /** Optional — omit when the host has no skills settings surface. */
555
650
  skillCatalog?: TSkillCatalog;
651
+ /** Optional — omit when the host has no sandboxes settings surface. */
652
+ sandboxCatalog?: TSandboxCatalog;
556
653
  }
557
654
 
558
655
  /**
@@ -26,7 +26,7 @@ export type TrueFoundryRuntimeExtras = {
26
26
  respondToToolApproval: (response: RespondToToolApprovalOptions) => void;
27
27
  respondToToolResponse: (response: RespondToToolResponseOptions) => void;
28
28
  resumeMcpAuth: () => Promise<void>;
29
- downloadSandboxFile: (path: string) => Promise<Blob>;
29
+ downloadSandboxFile: (req: { turnId: string; path: string }) => Promise<Blob>;
30
30
  cancel: () => Promise<void>;
31
31
  resetFromTurn: (turnId: string) => Promise<void>;
32
32
  reload: () => void;
@@ -152,18 +152,28 @@ function useTrueFoundryAgentRuntimeImpl(
152
152
  );
153
153
 
154
154
  const downloadSandboxFile = useCallback(
155
- async (path: string) => {
155
+ async ({ turnId, path }: { turnId: string; path: string }) => {
156
156
  if (server.downloadSandboxFile == null) {
157
157
  throw new Error(
158
158
  "Downloading a sandbox file requires AgentChatServer.downloadSandboxFile.",
159
159
  );
160
160
  }
161
+ if (sessionId == null) {
162
+ throw new Error(
163
+ "This session has not been saved yet, so its files cannot be downloaded.",
164
+ );
165
+ }
161
166
  if (sandboxId == null) {
162
167
  throw new Error("No sandbox is available yet for this session.");
163
168
  }
164
- return await server.downloadSandboxFile(sandboxId, { path });
169
+ return await server.downloadSandboxFile({
170
+ sessionId,
171
+ turnId,
172
+ sandboxId,
173
+ path,
174
+ });
165
175
  },
166
- [server, sandboxId],
176
+ [server, sessionId, sandboxId],
167
177
  );
168
178
 
169
179
  const draftExtras = useMemo(() => {