@reforma/types 1.4.0 → 1.5.0
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.
- package/dist/index.js +38 -0
- package/package.json +1 -1
- package/src/api/billing/contracts.ts +5 -0
- package/src/api/billing/credit-ledger-entry.ts +15 -0
- package/src/api/sandbox-clusters/create-sandbox-cluster.ts +2 -2
- package/src/api/sandbox-clusters/sandbox-cluster-provider.enum.ts +3 -1
- package/src/sandbox/ai.ts +60 -9
- package/src/sandbox/index.ts +1 -0
- package/src/sandbox/tools/agent.ts +4 -0
- package/src/sandbox/tools/get-background-result.ts +9 -0
- package/src/sandbox/ui-message.ts +31 -22
- package/src/sandbox/ui-parts.ts +49 -0
package/dist/index.js
CHANGED
|
@@ -342,6 +342,8 @@ let SandboxClusterNodeStatusEnum = /* @__PURE__ */ function(SandboxClusterNodeSt
|
|
|
342
342
|
// src/api/sandbox-clusters/sandbox-cluster-provider.enum.ts
|
|
343
343
|
let SandboxClusterProviderEnum = /* @__PURE__ */ function(SandboxClusterProviderEnum) {
|
|
344
344
|
SandboxClusterProviderEnum["rke2"] = "rke2";
|
|
345
|
+
/** Vercel Sandbox microVMs; overflow when no `rke2` cluster can take a sandbox. */
|
|
346
|
+
SandboxClusterProviderEnum["vercel"] = "vercel";
|
|
345
347
|
return SandboxClusterProviderEnum;
|
|
346
348
|
}({});
|
|
347
349
|
// src/api/sandbox-clusters/sandbox-cluster-status.enum.ts
|
|
@@ -546,6 +548,35 @@ let McpRuntimeStatus = /* @__PURE__ */ function(McpRuntimeStatus) {
|
|
|
546
548
|
McpRuntimeStatus["error"] = "error";
|
|
547
549
|
return McpRuntimeStatus;
|
|
548
550
|
}({});
|
|
551
|
+
// src/sandbox/ui-parts.ts
|
|
552
|
+
/** Whether the part is a data part (`data-*`). */
|
|
553
|
+
function isDataUIPart(part) {
|
|
554
|
+
return part.type.startsWith("data-");
|
|
555
|
+
}
|
|
556
|
+
/** Whether the part is a text part. */
|
|
557
|
+
function isTextUIPart(part) {
|
|
558
|
+
return part.type === "text";
|
|
559
|
+
}
|
|
560
|
+
/** Whether the part is a file part. */
|
|
561
|
+
function isFileUIPart(part) {
|
|
562
|
+
return part.type === "file";
|
|
563
|
+
}
|
|
564
|
+
/** Whether the part is a reasoning part. */
|
|
565
|
+
function isReasoningUIPart(part) {
|
|
566
|
+
return part.type === "reasoning";
|
|
567
|
+
}
|
|
568
|
+
/** Whether the part is a statically typed tool part (`tool-*`). */
|
|
569
|
+
function isStaticToolUIPart(part) {
|
|
570
|
+
return part.type.startsWith("tool-");
|
|
571
|
+
}
|
|
572
|
+
/** Whether the part is a static or dynamic tool part. */
|
|
573
|
+
function isToolUIPart(part) {
|
|
574
|
+
return isStaticToolUIPart(part) || part.type === "dynamic-tool";
|
|
575
|
+
}
|
|
576
|
+
/** Tool name of a static (`tool-<name>`) or dynamic tool part. */
|
|
577
|
+
function getToolName(part) {
|
|
578
|
+
return part.type === "dynamic-tool" ? part.toolName : part.type.split("-").slice(1).join("-");
|
|
579
|
+
}
|
|
549
580
|
// src/sandbox/usage-metadata.ts
|
|
550
581
|
/** Prompt-window buckets. Tool calls/results live in `conversation`, not `tools`. */
|
|
551
582
|
const CONTEXT_USAGE_BUCKET_IDS = [
|
|
@@ -613,7 +644,14 @@ exports.allocLayerCite = allocLayerCite;
|
|
|
613
644
|
exports.attachedLayerRefKey = attachedLayerRefKey;
|
|
614
645
|
exports.codeEditorStateKey = codeEditorStateKey;
|
|
615
646
|
exports.formatLayerCite = formatLayerCite;
|
|
647
|
+
exports.getToolName = getToolName;
|
|
648
|
+
exports.isDataUIPart = isDataUIPart;
|
|
649
|
+
exports.isFileUIPart = isFileUIPart;
|
|
650
|
+
exports.isReasoningUIPart = isReasoningUIPart;
|
|
616
651
|
exports.isScreenshotTool = isScreenshotTool;
|
|
652
|
+
exports.isStaticToolUIPart = isStaticToolUIPart;
|
|
653
|
+
exports.isTextUIPart = isTextUIPart;
|
|
654
|
+
exports.isToolUIPart = isToolUIPart;
|
|
617
655
|
exports.layerCiteBase = layerCiteBase;
|
|
618
656
|
exports.normalizeAttachedLayerRef = normalizeAttachedLayerRef;
|
|
619
657
|
exports.normalizeLayerSource = normalizeLayerSource;
|
package/package.json
CHANGED
|
@@ -81,6 +81,11 @@ export interface LlmUsageWebhookEvent {
|
|
|
81
81
|
id?: string;
|
|
82
82
|
litellm_call_id?: string;
|
|
83
83
|
call_type?: string;
|
|
84
|
+
/**
|
|
85
|
+
* LiteLLM session id: `x-litellm-trace-id`, or `litellm_session_id` on the
|
|
86
|
+
* Gemini pass-through, which drops `spend_logs_metadata`. Client-controlled.
|
|
87
|
+
*/
|
|
88
|
+
trace_id?: string | null;
|
|
84
89
|
status?: string;
|
|
85
90
|
/** Gateway-computed cost in USD for this request. */
|
|
86
91
|
response_cost?: number;
|
|
@@ -33,6 +33,21 @@ export interface CreditOperation {
|
|
|
33
33
|
/** Who spent the credits when this row is usage-linked; null for grants/purchases. */
|
|
34
34
|
userName: string | null;
|
|
35
35
|
userEmail: string | null;
|
|
36
|
+
/** The agent run behind a usage row; null for grants/purchases. */
|
|
37
|
+
usage: CreditUsageDetails | null;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/** The agent run a usage charge settled: where it ran and what the gateway saw. */
|
|
41
|
+
export interface CreditUsageDetails {
|
|
42
|
+
projectName: string | null;
|
|
43
|
+
threadTitle: string | null;
|
|
44
|
+
/** Gateway requests (model calls) in the run. */
|
|
45
|
+
requests: number;
|
|
46
|
+
inputTokens: number;
|
|
47
|
+
cacheReadTokens: number;
|
|
48
|
+
cacheWriteTokens: number;
|
|
49
|
+
outputTokens: number;
|
|
50
|
+
models: string[];
|
|
36
51
|
}
|
|
37
52
|
|
|
38
53
|
/** Paged customer ledger. */
|
|
@@ -8,8 +8,8 @@ export interface CreateSandboxCluster {
|
|
|
8
8
|
region: string;
|
|
9
9
|
apiUrl: string;
|
|
10
10
|
ingressDomain: string;
|
|
11
|
-
/** Plaintext kubeconfig YAML; encrypted at rest by the API. */
|
|
12
|
-
kubeconfig
|
|
11
|
+
/** Plaintext kubeconfig YAML; encrypted at rest by the API. Required for `rke2`. */
|
|
12
|
+
kubeconfig?: string;
|
|
13
13
|
status?: SandboxClusterStatusEnum;
|
|
14
14
|
maxSandboxes?: number;
|
|
15
15
|
/** Kubelet `Localhost` seccomp profile path for sandbox pods; omit for `Unconfined`. */
|
package/src/sandbox/ai.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { ThreadPlan } from './plans';
|
|
2
2
|
import type { ThreadTodo } from './todos';
|
|
3
3
|
import type { ThreadBackgroundJob } from './tools/get-background-result';
|
|
4
|
-
import type { ReformaUIMessage } from './ui-message';
|
|
4
|
+
import type { ReformaUIMessage, ReformaUserUIMessage } from './ui-message';
|
|
5
5
|
import type { ReformaContextUsage } from './usage-metadata';
|
|
6
6
|
|
|
7
7
|
export type ChatThreadSummaryDto = {
|
|
@@ -52,6 +52,63 @@ export type ChatRequestBody = {
|
|
|
52
52
|
/** Keep messages up to and including this id; drop the tail before merge. */
|
|
53
53
|
afterMessageId?: string;
|
|
54
54
|
runConfig?: ChatRunConfig;
|
|
55
|
+
/** "Send now" on a queued item: the item leaves the queue once this turn is accepted. */
|
|
56
|
+
queueItemId?: string;
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
/** `steer` reaches the running agent at its next step; `next-turn` starts the next run. */
|
|
60
|
+
export type ChatQueueItemMode = 'steer' | 'next-turn';
|
|
61
|
+
|
|
62
|
+
/** User input waiting on a thread run. Owned by the sandbox, in memory only. */
|
|
63
|
+
export type ChatQueueItem = {
|
|
64
|
+
/** Client-chosen id; adding the same id twice is a no-op. */
|
|
65
|
+
id: string;
|
|
66
|
+
mode: ChatQueueItemMode;
|
|
67
|
+
/** Ready-to-send user message. */
|
|
68
|
+
message: ReformaUserUIMessage;
|
|
69
|
+
runConfig?: ChatRunConfig;
|
|
70
|
+
/** Opaque composer state the client needs to reopen the item for editing. */
|
|
71
|
+
draft?: unknown;
|
|
72
|
+
/** Open in an editor. A locked steer is not taken, and a locked head holds the queue. */
|
|
73
|
+
editing: boolean;
|
|
74
|
+
createdAt: number;
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
/** Why the queue stopped draining. Only "Send now" on an item clears it. */
|
|
78
|
+
export type ChatQueuePause = {
|
|
79
|
+
reason: 'stopped' | 'failed';
|
|
80
|
+
error?: string;
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
/** Steers first, then next turns, each in send order. */
|
|
84
|
+
export type ChatQueueState = {
|
|
85
|
+
items: ChatQueueItem[];
|
|
86
|
+
paused?: ChatQueuePause;
|
|
87
|
+
};
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Sandbox sync value `chat-thread:<threadId>`: thread state without messages.
|
|
91
|
+
* A missing meta field means "unchanged": the idle snapshot of a thread the sandbox has not loaded carries none.
|
|
92
|
+
*/
|
|
93
|
+
export type ChatThreadStateSnapshot = {
|
|
94
|
+
/** Set while a run is in flight, including a run parked on the user. */
|
|
95
|
+
runId?: string;
|
|
96
|
+
queue: ChatQueueState;
|
|
97
|
+
title?: string;
|
|
98
|
+
todos?: ThreadTodo[];
|
|
99
|
+
/** `null`: the thread has no plan. */
|
|
100
|
+
plan?: ThreadPlan | null;
|
|
101
|
+
/** Running background jobs. */
|
|
102
|
+
backgroundJobs?: ThreadBackgroundJob[];
|
|
103
|
+
contextUsage?: ReformaContextUsage;
|
|
104
|
+
};
|
|
105
|
+
|
|
106
|
+
/** POST `/ai/threads/:threadId/queue` */
|
|
107
|
+
export type ChatQueueAddRequest = Pick<ChatQueueItem, 'id' | 'mode' | 'message' | 'runConfig' | 'draft'>;
|
|
108
|
+
|
|
109
|
+
/** PATCH `/ai/threads/:threadId/queue/:itemId`. `index` is the position inside the item's mode section. */
|
|
110
|
+
export type ChatQueuePatchRequest = Partial<Pick<ChatQueueItem, 'mode' | 'message' | 'draft' | 'editing'>> & {
|
|
111
|
+
index?: number;
|
|
55
112
|
};
|
|
56
113
|
|
|
57
114
|
/**
|
|
@@ -69,6 +126,8 @@ export type ChatThreadDetailDto = {
|
|
|
69
126
|
* The client may attempt stream resume (`GET …/chat/:threadId/stream`) only when this is true.
|
|
70
127
|
*/
|
|
71
128
|
hasActiveStream: boolean;
|
|
129
|
+
/** In-flight run, including one parked on the user. */
|
|
130
|
+
runId?: string;
|
|
72
131
|
/** Active todo list for this thread (empty when none). */
|
|
73
132
|
todos: ThreadTodo[];
|
|
74
133
|
/** Thread plan, when one exists. */
|
|
@@ -78,11 +137,3 @@ export type ChatThreadDetailDto = {
|
|
|
78
137
|
/** Latest prompt-window breakdown; omitted before the first model step. */
|
|
79
138
|
contextUsage?: ReformaContextUsage;
|
|
80
139
|
};
|
|
81
|
-
|
|
82
|
-
/** WS `thread-status` payload when a chat run starts or ends. */
|
|
83
|
-
export type ThreadStatusEvent = {
|
|
84
|
-
threadId: string;
|
|
85
|
-
isActive: boolean;
|
|
86
|
-
/** Set on subagent children so the client can ignore list refresh. */
|
|
87
|
-
parentThreadId?: string;
|
|
88
|
-
};
|
package/src/sandbox/index.ts
CHANGED
|
@@ -7,6 +7,8 @@ export type AgentToolInput = {
|
|
|
7
7
|
message: string;
|
|
8
8
|
/** `new` starts a child. `resume` continues the latest of this name. Omit for the agent default. */
|
|
9
9
|
resume?: 'new' | 'resume';
|
|
10
|
+
/** Run as a background job: a receipt now, the report as a notification later. Root agent only. */
|
|
11
|
+
background?: boolean;
|
|
10
12
|
};
|
|
11
13
|
|
|
12
14
|
export type AgentToolOutput = {
|
|
@@ -17,6 +19,8 @@ export type AgentToolOutput = {
|
|
|
17
19
|
/** Present when the subagent profile used structured output. */
|
|
18
20
|
data?: unknown;
|
|
19
21
|
error?: string;
|
|
22
|
+
/** Set when the subagent runs in the background: the receipt's job id. The report arrives later. */
|
|
23
|
+
jobId?: string;
|
|
20
24
|
};
|
|
21
25
|
|
|
22
26
|
export type AgentUITool = {
|
|
@@ -6,12 +6,21 @@ export type BackgroundResult = {
|
|
|
6
6
|
error?: string;
|
|
7
7
|
};
|
|
8
8
|
|
|
9
|
+
/** A background subagent's job: which agent runs, in which child thread, and whether it waits on the user. */
|
|
10
|
+
export type ThreadBackgroundAgent = {
|
|
11
|
+
name: string;
|
|
12
|
+
childThreadId: string;
|
|
13
|
+
waitingForUser?: boolean;
|
|
14
|
+
};
|
|
15
|
+
|
|
9
16
|
/** A background job on this thread. `output` / `error` appear when it finishes. */
|
|
10
17
|
export type ThreadBackgroundJob = {
|
|
11
18
|
id: string;
|
|
12
19
|
description: string;
|
|
13
20
|
output?: unknown;
|
|
14
21
|
error?: string;
|
|
22
|
+
/** Set when the job is a subagent; commands and other work leave it out. */
|
|
23
|
+
agent?: ThreadBackgroundAgent;
|
|
15
24
|
};
|
|
16
25
|
|
|
17
26
|
/** Matches sandbox `GetBackgroundResult` tool schemas. */
|
|
@@ -4,8 +4,6 @@ import type { SelectedLayerContext } from '../layers';
|
|
|
4
4
|
import type { CodeEditorState } from '../theia/bridge';
|
|
5
5
|
import type { ReformaTurnGitCommit } from './git/commit';
|
|
6
6
|
import type { VersionStatus } from './git/version';
|
|
7
|
-
import type { ThreadPlan } from './plans';
|
|
8
|
-
import type { ThreadTodo } from './todos';
|
|
9
7
|
import type {
|
|
10
8
|
AgentUITool,
|
|
11
9
|
AskUserQuestionUITool,
|
|
@@ -48,7 +46,6 @@ import type {
|
|
|
48
46
|
RegisterLocalFontUITool,
|
|
49
47
|
RemoveFileUITool,
|
|
50
48
|
SkillUITool,
|
|
51
|
-
ThreadBackgroundJob,
|
|
52
49
|
TodoWriteUITool,
|
|
53
50
|
WaitingUITool,
|
|
54
51
|
WriteUITool,
|
|
@@ -162,6 +159,12 @@ export type ReformaChatMessageMetadata = {
|
|
|
162
159
|
/** User stopped this assistant turn. Persisted with the message. */
|
|
163
160
|
stopReason?: 'cancelled';
|
|
164
161
|
|
|
162
|
+
/**
|
|
163
|
+
* The user's ChatGPT plan or API key could not pay for this turn.
|
|
164
|
+
* The composer shows a limit plate instead of the generic error row.
|
|
165
|
+
*/
|
|
166
|
+
payerFailure?: PayerFailure;
|
|
167
|
+
|
|
165
168
|
/** Pre/Post/Stop hook context frozen for this assistant turn (server-tagged before persist). */
|
|
166
169
|
turnHookContext?: string;
|
|
167
170
|
};
|
|
@@ -182,7 +185,6 @@ export type RunCreditsSyncEntry = {
|
|
|
182
185
|
*/
|
|
183
186
|
|
|
184
187
|
export type ThreadUpdateData = {
|
|
185
|
-
title?: string;
|
|
186
188
|
/** Emitted when the server assigns a new thread id (draft first message). */
|
|
187
189
|
threadId?: string;
|
|
188
190
|
};
|
|
@@ -213,21 +215,6 @@ export type ProjectUpdateData = {
|
|
|
213
215
|
screenshotUrl?: string | null;
|
|
214
216
|
};
|
|
215
217
|
|
|
216
|
-
/** Transient UI stream payload (`data-thread-todos`); not stored on messages */
|
|
217
|
-
export type ThreadTodosUpdateData = {
|
|
218
|
-
todos: ThreadTodo[];
|
|
219
|
-
};
|
|
220
|
-
|
|
221
|
-
/** Transient UI stream payload (`data-thread-plan`); not stored on messages */
|
|
222
|
-
export type ThreadPlanUpdateData = {
|
|
223
|
-
plan: ThreadPlan;
|
|
224
|
-
};
|
|
225
|
-
|
|
226
|
-
/** Transient UI stream payload (`data-thread-background-jobs`); not stored on messages */
|
|
227
|
-
export type ThreadBackgroundJobsUpdateData = {
|
|
228
|
-
backgroundJobs: ThreadBackgroundJob[];
|
|
229
|
-
};
|
|
230
|
-
|
|
231
218
|
/**
|
|
232
219
|
* Finished background commands, stored on the assistant message (`data-background-result`).
|
|
233
220
|
* `text` is the model note frozen at delivery time.
|
|
@@ -238,6 +225,16 @@ export type BackgroundResultNoticeData = {
|
|
|
238
225
|
text: string;
|
|
239
226
|
};
|
|
240
227
|
|
|
228
|
+
/**
|
|
229
|
+
* A user message the running agent took between steps (`data-user-steer`), stored on the
|
|
230
|
+
* assistant message at that position. The model sees it as a user turn there.
|
|
231
|
+
*/
|
|
232
|
+
export type UserSteerData = {
|
|
233
|
+
message: ReformaUserUIMessage;
|
|
234
|
+
/** Unix ms when the agent took the steer. Missing on parts stored before it was recorded. */
|
|
235
|
+
takenAt?: number;
|
|
236
|
+
};
|
|
237
|
+
|
|
241
238
|
/** Transient UI stream payload (`data-version-status`); mirrors WS `version-status`. */
|
|
242
239
|
export type VersionStatusData = VersionStatus;
|
|
243
240
|
|
|
@@ -308,15 +305,26 @@ export interface ReformaChatDataContextCompactionPayload {
|
|
|
308
305
|
resumeMessageId?: string;
|
|
309
306
|
}
|
|
310
307
|
|
|
308
|
+
/**
|
|
309
|
+
* ChatGPT plan or a user API key could not pay.
|
|
310
|
+
* Streamed as transient `data-payer-failure`, and saved on the assistant message.
|
|
311
|
+
*/
|
|
312
|
+
export type PayerFailure = {
|
|
313
|
+
kind: 'limit' | 'access';
|
|
314
|
+
/** `chatgpt` is an OAuth plan. `api_key` is the user's provider key. */
|
|
315
|
+
payer: 'chatgpt' | 'api_key';
|
|
316
|
+
providerId: 'openai' | 'anthropic' | 'google';
|
|
317
|
+
/** Calendar day (`YYYY-MM-DD`) the provider named. Absent when they did not. */
|
|
318
|
+
resetAt?: string;
|
|
319
|
+
};
|
|
320
|
+
|
|
311
321
|
/** Maps `data-*` suffix keys to payload shapes for {@link ReformaUIMessage}. */
|
|
312
322
|
export type ReformaChatDataTypes = {
|
|
313
323
|
'thread-update': ThreadUpdateData;
|
|
314
324
|
'project-update': ProjectUpdateData;
|
|
315
325
|
'version-status': VersionStatusData;
|
|
316
|
-
'thread-todos': ThreadTodosUpdateData;
|
|
317
|
-
'thread-plan': ThreadPlanUpdateData;
|
|
318
|
-
'thread-background-jobs': ThreadBackgroundJobsUpdateData;
|
|
319
326
|
'background-result': BackgroundResultNoticeData;
|
|
327
|
+
'user-steer': UserSteerData;
|
|
320
328
|
'tool-metadata': ToolMetadataUpdateData;
|
|
321
329
|
'client-tool': ClientToolRequestData;
|
|
322
330
|
'tool-execution-progress': ReformaChatDataToolExecutionProgressPayload;
|
|
@@ -324,6 +332,7 @@ export type ReformaChatDataTypes = {
|
|
|
324
332
|
'redacted-reasoning': ReformaChatDataRedactedReasoningPayload;
|
|
325
333
|
'context-compaction': ReformaChatDataContextCompactionPayload;
|
|
326
334
|
'subagent-status': SubagentStatusUpdateData;
|
|
335
|
+
'payer-failure': PayerFailure;
|
|
327
336
|
};
|
|
328
337
|
|
|
329
338
|
// --- Tool UI parts (third `UIMessage` generic) ---
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
DataUIPart,
|
|
3
|
+
DynamicToolUIPart,
|
|
4
|
+
FileUIPart,
|
|
5
|
+
ReasoningUIPart,
|
|
6
|
+
TextUIPart,
|
|
7
|
+
ToolUIPart,
|
|
8
|
+
UIDataTypes,
|
|
9
|
+
UIMessagePart,
|
|
10
|
+
UITools,
|
|
11
|
+
} from 'ai' with { 'resolution-mode': 'import' };
|
|
12
|
+
|
|
13
|
+
// Same contracts as the `ai` part guards. Importing them from `ai` evaluates the whole
|
|
14
|
+
// SDK and its zod schemas, which chat UI outside the editor does not need.
|
|
15
|
+
|
|
16
|
+
/** Whether the part is a data part (`data-*`). */
|
|
17
|
+
export function isDataUIPart<DATA_TYPES extends UIDataTypes>(part: UIMessagePart<DATA_TYPES, UITools>): part is DataUIPart<DATA_TYPES> {
|
|
18
|
+
return part.type.startsWith('data-');
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/** Whether the part is a text part. */
|
|
22
|
+
export function isTextUIPart(part: UIMessagePart<UIDataTypes, UITools>): part is TextUIPart {
|
|
23
|
+
return part.type === 'text';
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/** Whether the part is a file part. */
|
|
27
|
+
export function isFileUIPart(part: UIMessagePart<UIDataTypes, UITools>): part is FileUIPart {
|
|
28
|
+
return part.type === 'file';
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/** Whether the part is a reasoning part. */
|
|
32
|
+
export function isReasoningUIPart(part: UIMessagePart<UIDataTypes, UITools>): part is ReasoningUIPart {
|
|
33
|
+
return part.type === 'reasoning';
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/** Whether the part is a statically typed tool part (`tool-*`). */
|
|
37
|
+
export function isStaticToolUIPart<TOOLS extends UITools>(part: UIMessagePart<UIDataTypes, TOOLS>): part is ToolUIPart<TOOLS> {
|
|
38
|
+
return part.type.startsWith('tool-');
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/** Whether the part is a static or dynamic tool part. */
|
|
42
|
+
export function isToolUIPart<TOOLS extends UITools>(part: UIMessagePart<UIDataTypes, TOOLS>): part is ToolUIPart<TOOLS> | DynamicToolUIPart {
|
|
43
|
+
return isStaticToolUIPart(part) || part.type === 'dynamic-tool';
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/** Tool name of a static (`tool-<name>`) or dynamic tool part. */
|
|
47
|
+
export function getToolName(part: ToolUIPart<UITools> | DynamicToolUIPart): string {
|
|
48
|
+
return part.type === 'dynamic-tool' ? part.toolName : part.type.split('-').slice(1).join('-');
|
|
49
|
+
}
|