@truefoundry/assistant-ui-runtime 0.1.6-rc.0 → 0.1.6

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.
@@ -0,0 +1,135 @@
1
+ /**
2
+ * TrueFoundry-specific type extensions over the runtime's generic bases.
3
+ *
4
+ * The runtime defines minimal bases (SkillMount = {id,name}, McpServerMount =
5
+ * {id,name}, AgentSpec.config = unknown, etc.) that hosts extend via generics.
6
+ *
7
+ * This file builds the concrete TrueFoundry types by:
8
+ * - Importing the runtime's AgentSpec as the base to extend.
9
+ * - Importing concrete sub-types from the gateway SDK namespace rather than
10
+ * re-defining them — they're purely gateway concepts.
11
+ * - Composing a TfyAgentSpec that satisfies both the runtime's
12
+ * `TSpec extends AgentSpec` constraint and the gateway SDK's AgentSpec shape.
13
+ */
14
+
15
+ import type {
16
+ AgentSpec,
17
+ CreateSessionRequest,
18
+ ListSessionsParams,
19
+ Session,
20
+ Turn,
21
+ TurnState,
22
+ } from "../../server/types.js";
23
+ import type { TruefoundryGatewayApi } from "truefoundry-gateway-sdk";
24
+
25
+ // ---------------------------------------------------------------------------
26
+ // Re-exports — gateway sub-types surfaced for host convenience
27
+ // ---------------------------------------------------------------------------
28
+
29
+ export type TfyRuntimeConfig = TruefoundryGatewayApi.RuntimeConfig;
30
+ export type TfyResponseFormat = TruefoundryGatewayApi.ResponseFormat;
31
+ export type TfyModelParams = TruefoundryGatewayApi.ModelParams;
32
+ export type TfySubject = TruefoundryGatewayApi.Subject;
33
+
34
+ // ---------------------------------------------------------------------------
35
+ // Tool selector helpers (mirrors gateway SDK enum values)
36
+ // ---------------------------------------------------------------------------
37
+
38
+ export type ToolsSelectorTag = "@all" | "@read-only";
39
+ export type RequireApprovalToolsSelectorTag = "@all" | "@write" | "@destructive";
40
+ export type ToolsSelectorItem = ToolsSelectorTag | string;
41
+ export type RequireApprovalToolSelectorItem = RequireApprovalToolsSelectorTag | string;
42
+
43
+ // ---------------------------------------------------------------------------
44
+ // Runtime mount bases — derived from AgentSpec, which the runtime does export
45
+ // (SkillMount / McpServerMount themselves are not part of its public API).
46
+ // ---------------------------------------------------------------------------
47
+
48
+ type RuntimeSkillMount = NonNullable<NonNullable<AgentSpec["skills"]>[number]>;
49
+ type RuntimeMcpServerMount = NonNullable<NonNullable<AgentSpec["mcpServers"]>[number]>;
50
+
51
+ // ---------------------------------------------------------------------------
52
+ // Mounts — runtime base + gateway fields (fqn, type, url, tool selectors)
53
+ // ---------------------------------------------------------------------------
54
+
55
+ // Both gateway types are unions (git vs registry source, inline vs registry
56
+ // server), so these must be intersections — an interface cannot `extends` a
57
+ // union, and doing so silently degrades to the runtime base under skipLibCheck.
58
+ export type TfySkillMount = RuntimeSkillMount & TruefoundryGatewayApi.SkillMount;
59
+
60
+ export type TfyMcpServerMount = RuntimeMcpServerMount & TruefoundryGatewayApi.McpServer;
61
+
62
+ // ---------------------------------------------------------------------------
63
+ // AgentSpec — the concrete TrueFoundry agent definition
64
+ // ---------------------------------------------------------------------------
65
+
66
+ export interface TfyAgentSpec extends AgentSpec {
67
+ model: TruefoundryGatewayApi.Model;
68
+ skills?: TfySkillMount[];
69
+ mcpServers?: TfyMcpServerMount[];
70
+ config?: TruefoundryGatewayApi.RuntimeConfig;
71
+ responseFormat?: TruefoundryGatewayApi.ResponseFormat;
72
+ messages?: TruefoundryGatewayApi.AgentSpecUserMessage[];
73
+ }
74
+
75
+ // ---------------------------------------------------------------------------
76
+ // Turn — runtime base narrowed to the gateway's concrete state shapes
77
+ // ---------------------------------------------------------------------------
78
+
79
+ export type TfyTurnCancelledReason = TruefoundryGatewayApi.TurnStateCancelledReason;
80
+ export type TfyTurnStateDoneOutput = TruefoundryGatewayApi.TurnStateDoneOutput;
81
+
82
+ type RuntimeTurnStateDone = Extract<TurnState, { status: "done" }>;
83
+ type RuntimeTurnStateCancelled = Extract<TurnState, { status: "cancelled" }>;
84
+
85
+ /**
86
+ * The runtime types `output` as `unknown` and `reason` as bare `string`. The
87
+ * gateway sends a model message and one of four reasons — `cancelled-for-next-turn`
88
+ * in particular is routine and should not render like a failure.
89
+ */
90
+ export type TfyTurnState =
91
+ | Exclude<TurnState, { status: "done" | "cancelled" }>
92
+ | (Omit<RuntimeTurnStateDone, "output"> & { output?: TfyTurnStateDoneOutput })
93
+ | (Omit<RuntimeTurnStateCancelled, "reason"> & { reason: TfyTurnCancelledReason });
94
+
95
+ export interface TfyTurn extends Turn {
96
+ state: TfyTurnState;
97
+ createdBySubject: TfySubject;
98
+ }
99
+
100
+ // ---------------------------------------------------------------------------
101
+ // Session and request params — runtime bases + gateway-only fields
102
+ // ---------------------------------------------------------------------------
103
+
104
+ export interface TfySession<TSpec extends TfyAgentSpec = TfyAgentSpec>
105
+ extends Session<TSpec> {
106
+ createdBySubject: TfySubject;
107
+ }
108
+
109
+ export interface TfyCreateSessionRequest<TSpec extends TfyAgentSpec = TfyAgentSpec>
110
+ extends CreateSessionRequest<TSpec> {
111
+ /** Sent as `x-tfy-metadata`, persisted server-side as `request_metadata`. */
112
+ tfyMetadata?: string;
113
+ }
114
+
115
+ export interface TfyListSessionsParams extends ListSessionsParams {
116
+ /** Inclusive upper bound on `createdAt` (ISO-8601). */
117
+ endTimestamp?: string;
118
+ }
119
+
120
+ // ---------------------------------------------------------------------------
121
+ // Event-side types.
122
+ //
123
+ // These do NOT flow through AgentChatServer — its listEvents / listTurnEvents /
124
+ // subscribeToTurn / prepareAndExecuteTurn signatures hardcode the runtime's
125
+ // event types with no generic to override. Hosts narrow at the point of use
126
+ // with the guards in `guards.ts`.
127
+ // ---------------------------------------------------------------------------
128
+
129
+ export type TfyToolInfo = TruefoundryGatewayApi.ToolInfo;
130
+ export type TfySystemToolInfo = TruefoundryGatewayApi.TrueFoundrySystemToolInfo;
131
+ export type TfyMcpToolInfo = TruefoundryGatewayApi.McpToolInfo;
132
+ export type TfyModelMessageUsage = TruefoundryGatewayApi.ModelMessageUsage;
133
+ export type TfyFinishReason = TruefoundryGatewayApi.FinishReason;
134
+ export type TfyThreadState = TruefoundryGatewayApi.ThreadState;
135
+ export type TfyMcpServerInitInfo = TruefoundryGatewayApi.McpServerInitInfo;
@@ -0,0 +1,164 @@
1
+ /**
2
+ * Compile-time assertions for the mount types in `types.ts`.
3
+ *
4
+ * TfySkillMount / TfyMcpServerMount are built by indexing into the runtime's
5
+ * exported AgentSpec. If that indexed access ever degrades to `unknown`, the
6
+ * intersection becomes a silent no-op and the gateway fields disappear without
7
+ * any error. These assertions fail `pnpm typecheck` if that happens.
8
+ *
9
+ * Not an entry point — tsup only bundles src/index.ts.
10
+ */
11
+
12
+ import type { AgentSpec } from "../../server/types.js";
13
+ import type { createTrueFoundryChatServer } from "./index.js";
14
+ import type {
15
+ TfyAgentSpec,
16
+ TfyFinishReason,
17
+ TfyMcpServerMount,
18
+ TfySkillMount,
19
+ TfySubject,
20
+ TfyTurn,
21
+ } from "./types.js";
22
+
23
+ // Both gateway source variants must survive the intersection with the base.
24
+ export const registrySkill: TfySkillMount = {
25
+ type: "truefoundry-skills-registry",
26
+ fqn: "tfy:skill:v1",
27
+ preload: true,
28
+ };
29
+
30
+ export const gitSkill: TfySkillMount = {
31
+ type: "git",
32
+ url: "https://github.com/acme/skills",
33
+ name: "reviewer",
34
+ ref: "main",
35
+ };
36
+
37
+ // @ts-expect-error the type discriminant must survive the intersection
38
+ export const skillWithoutType: TfySkillMount = { fqn: "a" };
39
+
40
+ // @ts-expect-error registry variant requires fqn
41
+ export const registrySkillWithoutFqn: TfySkillMount = {
42
+ type: "truefoundry-skills-registry",
43
+ };
44
+
45
+ export const registeredMcp: TfyMcpServerMount = {
46
+ name: "github",
47
+ type: "truefoundry-mcp-registry",
48
+ enableTools: ["@read-only"],
49
+ requireApprovalForTools: ["@destructive"],
50
+ };
51
+
52
+ export const inlineMcp: TfyMcpServerMount = {
53
+ name: "custom",
54
+ type: "inline",
55
+ url: "https://example.com/mcp",
56
+ };
57
+
58
+ // @ts-expect-error the type discriminant must survive the intersection
59
+ export const mcpWithoutType: TfyMcpServerMount = { name: "a" };
60
+
61
+ // @ts-expect-error inline variant requires url
62
+ export const inlineMcpWithoutUrl: TfyMcpServerMount = {
63
+ name: "a",
64
+ type: "inline",
65
+ };
66
+
67
+ // The gateway's own spec — a registry skill carries neither `id` nor `name` —
68
+ // must satisfy the runtime base. A base that rejects it makes every server
69
+ // response unassignable, which is the regression these mounts are guarding.
70
+ declare const gatewayShapedSpec: TfyAgentSpec;
71
+ export const asRuntimeSpec: AgentSpec = gatewayShapedSpec;
72
+
73
+ // ---------------------------------------------------------------------------
74
+ // Generic TSpec — a host extension must reach the returned server's types,
75
+ // and the bare (defaulted) form must keep working.
76
+ // ---------------------------------------------------------------------------
77
+
78
+ interface HostSpec extends TfyAgentSpec {
79
+ workspaceId: string;
80
+ }
81
+
82
+ type HostServer = ReturnType<typeof createTrueFoundryChatServer<HostSpec>>;
83
+ type DefaultServer = ReturnType<typeof createTrueFoundryChatServer>;
84
+
85
+ declare const hostSession: Awaited<ReturnType<HostServer["getSession"]>>;
86
+ declare const defaultSession: Awaited<ReturnType<DefaultServer["getSession"]>>;
87
+
88
+ // Host field is visible on the spec returned by the generic server.
89
+ export const workspaceId: string | undefined = hostSession.agentSpec?.workspaceId;
90
+
91
+ // Gateway fields still flow through alongside it.
92
+ export const hostSkills: TfySkillMount[] | undefined = hostSession.agentSpec?.skills;
93
+
94
+ // @ts-expect-error the default instantiation must NOT have the host's field
95
+ export const leakedWorkspaceId = defaultSession.agentSpec?.workspaceId;
96
+
97
+ // Host spec is accepted where the server expects one.
98
+ export declare function createHostSession(
99
+ server: HostServer,
100
+ spec: HostSpec,
101
+ ): ReturnType<HostServer["createSession"]>;
102
+
103
+ // ---------------------------------------------------------------------------
104
+ // Turn — the gateway narrowings must not collapse back into the runtime's
105
+ // `unknown` output / bare-`string` reason.
106
+ // ---------------------------------------------------------------------------
107
+
108
+ const subject: TfySubject = {
109
+ subjectId: "u-1",
110
+ subjectType: "user",
111
+ subjectSlug: "someone",
112
+ };
113
+
114
+ export const cancelledTurn: TfyTurn = {
115
+ id: "turn-1",
116
+ sessionId: "session-1",
117
+ state: {
118
+ status: "cancelled",
119
+ reason: "cancelled-for-next-turn",
120
+ completedAt: "2026-01-01T00:00:00Z",
121
+ },
122
+ createdBySubject: subject,
123
+ createdAt: "2026-01-01T00:00:00Z",
124
+ };
125
+
126
+ export const freeformCancelReason: TfyTurn = {
127
+ ...cancelledTurn,
128
+ // @ts-expect-error reason is one of four gateway values, not any string
129
+ state: { status: "cancelled", reason: "nope", completedAt: "2026-01-01T00:00:00Z" },
130
+ };
131
+
132
+ // @ts-expect-error createdBySubject is required — the gateway always sends it
133
+ export const turnWithoutSubject: TfyTurn = {
134
+ id: "turn-1",
135
+ sessionId: "session-1",
136
+ state: { status: "running" },
137
+ createdAt: "2026-01-01T00:00:00Z",
138
+ };
139
+
140
+ declare const doneTurn: Extract<TfyTurn["state"], { status: "done" }>;
141
+
142
+ // `output` is the gateway's model message; on the runtime base this is `unknown`
143
+ // and would not permit a property access at all.
144
+ export const doneFinishReason: TfyFinishReason | null | undefined =
145
+ doneTurn.output?.finishReason;
146
+ export const doneTokens: number | undefined = doneTurn.output?.usage?.inputTokens;
147
+
148
+ // ---------------------------------------------------------------------------
149
+ // Session / request params — gateway-only fields reach the server methods.
150
+ // ---------------------------------------------------------------------------
151
+
152
+ export const sessionSubject: TfySubject = hostSession.createdBySubject;
153
+
154
+ export const withMetadata: Parameters<DefaultServer["createSession"]>[0] = {
155
+ agentName: "agent",
156
+ tfyMetadata: JSON.stringify({ tenant: "acme" }),
157
+ };
158
+
159
+ export const withEndTimestamp: NonNullable<
160
+ Parameters<DefaultServer["listSessions"]>[0]
161
+ > = {
162
+ startTimestamp: "2026-01-01T00:00:00Z",
163
+ endTimestamp: "2026-02-01T00:00:00Z",
164
+ };
@@ -53,17 +53,23 @@ export type SearchAgentSelectorParams = {
53
53
  // Mounts — neutral SDK base (host extends with backend-specific fields)
54
54
  // ---------------------------------------------------------------------------
55
55
 
56
- /** Skill mount base written to AgentSpec.skills[]. Host extends for fqn, preload, etc. */
57
- export interface SkillMount {
58
- id: string;
59
- name: string;
60
- }
56
+ /**
57
+ * Mounts written to AgentSpec.skills[] / AgentSpec.mcpServers[].
58
+ *
59
+ * These are opaque to the runtime — it stores and forwards them but never reads
60
+ * a field, and the backend owns the shape (the gateway identifies a skill by
61
+ * `fqn`, with no `id` or `name` anywhere). So the base constrains only that a
62
+ * mount is an object; hosts intersect their concrete mount type over it, as
63
+ * `TfySkillMount` / `TfyMcpServerMount` do in the gateway adapter.
64
+ *
65
+ * Naming a field here would not just be unread, it would be wrong: a base with
66
+ * required fields rejects the backend's own payloads, and one with only optional
67
+ * fields is a weak type, which TypeScript rejects for a source that shares no
68
+ * property with it — the gateway's registry skill shares none.
69
+ */
70
+ export type SkillMount = object;
61
71
 
62
- /** MCP server mount base written to AgentSpec.mcpServers[]. Host extends for type, enableTools, etc. */
63
- export interface McpServerMount {
64
- id: string;
65
- name: string;
66
- }
72
+ export type McpServerMount = object;
67
73
 
68
74
  // ---------------------------------------------------------------------------
69
75
  // AgentSpec — model + skills + mcpServers on base; host widens the rest