@nestm/storage 0.1.0-alpha.5 → 0.1.0-alpha.7
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/CHANGELOG.md +26 -0
- package/README.md +158 -92
- package/SECURITY.md +40 -0
- package/dist/ai-sdk/ai-sdk-workspace-tools.d.ts +63 -0
- package/dist/ai-sdk/ai-sdk-workspace-tools.d.ts.map +1 -0
- package/dist/ai-sdk/ai-sdk-workspace-tools.js +383 -0
- package/dist/ai-sdk/ai-sdk-workspace-tools.js.map +1 -0
- package/dist/ai-sdk/index.d.ts +2 -0
- package/dist/ai-sdk/index.d.ts.map +1 -0
- package/dist/ai-sdk/index.js +2 -0
- package/dist/ai-sdk/index.js.map +1 -0
- package/dist/files-sdk/files-sdk.driver.d.ts +16 -2
- package/dist/files-sdk/files-sdk.driver.d.ts.map +1 -1
- package/dist/files-sdk/files-sdk.driver.js +159 -0
- package/dist/files-sdk/files-sdk.driver.js.map +1 -1
- package/dist/files-sdk/fs/index.d.ts +21 -2
- package/dist/files-sdk/fs/index.d.ts.map +1 -1
- package/dist/files-sdk/fs/index.js +600 -1
- package/dist/files-sdk/fs/index.js.map +1 -1
- package/dist/files-sdk/index.d.ts +1 -1
- package/dist/files-sdk/index.d.ts.map +1 -1
- package/dist/files-sdk/index.js.map +1 -1
- package/dist/files-sdk/provider/index.d.ts.map +1 -1
- package/dist/files-sdk/provider/index.js +7 -0
- package/dist/files-sdk/provider/index.js.map +1 -1
- package/dist/files-sdk/s3/index.d.ts +12 -3
- package/dist/files-sdk/s3/index.d.ts.map +1 -1
- package/dist/files-sdk/s3/index.js +203 -37
- package/dist/files-sdk/s3/index.js.map +1 -1
- package/dist/storage.client.d.ts +5 -1
- package/dist/storage.client.d.ts.map +1 -1
- package/dist/storage.client.js +49 -0
- package/dist/storage.client.js.map +1 -1
- package/dist/storage.driver.d.ts +3 -1
- package/dist/storage.driver.d.ts.map +1 -1
- package/dist/storage.driver.js.map +1 -1
- package/dist/storage.service.d.ts.map +1 -1
- package/dist/storage.service.js +28 -3
- package/dist/storage.service.js.map +1 -1
- package/dist/storage.types.d.ts +36 -0
- package/dist/storage.types.d.ts.map +1 -1
- package/dist/storage.types.js.map +1 -1
- package/dist/workspace/index.d.ts +4 -0
- package/dist/workspace/index.d.ts.map +1 -0
- package/dist/workspace/index.js +4 -0
- package/dist/workspace/index.js.map +1 -0
- package/dist/workspace/storage-workspace.cursor.d.ts +6 -0
- package/dist/workspace/storage-workspace.cursor.d.ts.map +1 -0
- package/dist/workspace/storage-workspace.cursor.js +45 -0
- package/dist/workspace/storage-workspace.cursor.js.map +1 -0
- package/dist/workspace/storage-workspace.d.ts +6 -0
- package/dist/workspace/storage-workspace.d.ts.map +1 -0
- package/dist/workspace/storage-workspace.error.d.ts +28 -0
- package/dist/workspace/storage-workspace.error.d.ts.map +1 -0
- package/dist/workspace/storage-workspace.error.js +67 -0
- package/dist/workspace/storage-workspace.error.js.map +1 -0
- package/dist/workspace/storage-workspace.js +747 -0
- package/dist/workspace/storage-workspace.js.map +1 -0
- package/dist/workspace/storage-workspace.path.d.ts +9 -0
- package/dist/workspace/storage-workspace.path.d.ts.map +1 -0
- package/dist/workspace/storage-workspace.path.js +68 -0
- package/dist/workspace/storage-workspace.path.js.map +1 -0
- package/dist/workspace/storage-workspace.types.d.ts +97 -0
- package/dist/workspace/storage-workspace.types.d.ts.map +1 -0
- package/dist/workspace/storage-workspace.types.js +20 -0
- package/dist/workspace/storage-workspace.types.js.map +1 -0
- package/package.json +23 -20
- package/dist/artifacts/artifact-storage.d.ts +0 -161
- package/dist/artifacts/artifact-storage.d.ts.map +0 -1
- package/dist/artifacts/artifact-storage.js +0 -452
- package/dist/artifacts/artifact-storage.js.map +0 -1
- package/dist/artifacts/crypto/codec.d.ts +0 -44
- package/dist/artifacts/crypto/codec.d.ts.map +0 -1
- package/dist/artifacts/crypto/codec.js +0 -273
- package/dist/artifacts/crypto/codec.js.map +0 -1
- package/dist/artifacts/crypto/context.d.ts +0 -48
- package/dist/artifacts/crypto/context.d.ts.map +0 -1
- package/dist/artifacts/crypto/context.js +0 -37
- package/dist/artifacts/crypto/context.js.map +0 -1
- package/dist/artifacts/crypto/dek-cache.d.ts +0 -38
- package/dist/artifacts/crypto/dek-cache.d.ts.map +0 -1
- package/dist/artifacts/crypto/dek-cache.js +0 -137
- package/dist/artifacts/crypto/dek-cache.js.map +0 -1
- package/dist/artifacts/crypto/index.d.ts +0 -25
- package/dist/artifacts/crypto/index.d.ts.map +0 -1
- package/dist/artifacts/crypto/index.js +0 -29
- package/dist/artifacts/crypto/index.js.map +0 -1
- package/dist/artifacts/crypto/key-provider.d.ts +0 -44
- package/dist/artifacts/crypto/key-provider.d.ts.map +0 -1
- package/dist/artifacts/crypto/key-provider.js +0 -36
- package/dist/artifacts/crypto/key-provider.js.map +0 -1
- package/dist/artifacts/crypto/kms-provider.d.ts +0 -16
- package/dist/artifacts/crypto/kms-provider.d.ts.map +0 -1
- package/dist/artifacts/crypto/kms-provider.js +0 -90
- package/dist/artifacts/crypto/kms-provider.js.map +0 -1
- package/dist/artifacts/crypto/local-provider.d.ts +0 -17
- package/dist/artifacts/crypto/local-provider.d.ts.map +0 -1
- package/dist/artifacts/crypto/local-provider.js +0 -58
- package/dist/artifacts/crypto/local-provider.js.map +0 -1
- package/dist/artifacts/env-config.d.ts +0 -15
- package/dist/artifacts/env-config.d.ts.map +0 -1
- package/dist/artifacts/env-config.js +0 -95
- package/dist/artifacts/env-config.js.map +0 -1
- package/dist/artifacts/index.d.ts +0 -6
- package/dist/artifacts/index.d.ts.map +0 -1
- package/dist/artifacts/index.js +0 -6
- package/dist/artifacts/index.js.map +0 -1
- package/dist/artifacts/nest/index.d.ts +0 -29
- package/dist/artifacts/nest/index.d.ts.map +0 -1
- package/dist/artifacts/nest/index.js +0 -129
- package/dist/artifacts/nest/index.js.map +0 -1
- package/dist/artifacts/object-store.d.ts +0 -27
- package/dist/artifacts/object-store.d.ts.map +0 -1
- package/dist/artifacts/object-store.js +0 -198
- package/dist/artifacts/object-store.js.map +0 -1
- package/dist/artifacts/storage-driver.d.ts +0 -48
- package/dist/artifacts/storage-driver.d.ts.map +0 -1
- package/dist/artifacts/storage-driver.js +0 -89
- package/dist/artifacts/storage-driver.js.map +0 -1
|
@@ -0,0 +1,383 @@
|
|
|
1
|
+
import { tool } from 'ai';
|
|
2
|
+
import { z } from 'zod';
|
|
3
|
+
import { isStorageWorkspaceError, } from '../workspace/index.js';
|
|
4
|
+
import { StorageErrorCode, isStorageError, } from '../storage.error.js';
|
|
5
|
+
export const AI_SDK_WORKSPACE_TOOL_NAMES = [
|
|
6
|
+
'workspace_list',
|
|
7
|
+
'workspace_stat',
|
|
8
|
+
'workspace_read_file',
|
|
9
|
+
'workspace_search',
|
|
10
|
+
'workspace_write_file',
|
|
11
|
+
'workspace_copy_file',
|
|
12
|
+
'workspace_move_file',
|
|
13
|
+
'workspace_delete_file',
|
|
14
|
+
];
|
|
15
|
+
const SAFE_ERROR_MESSAGES = {
|
|
16
|
+
[StorageErrorCode.NOT_FOUND]: 'The requested workspace path was not found.',
|
|
17
|
+
[StorageErrorCode.UNAUTHORIZED]: 'This operation is not permitted in the workspace.',
|
|
18
|
+
[StorageErrorCode.CONFLICT]: 'The operation conflicts with current workspace state. Refresh metadata and retry with the current ETag or a new destination.',
|
|
19
|
+
[StorageErrorCode.READ_ONLY]: 'This operation is not permitted in the workspace.',
|
|
20
|
+
[StorageErrorCode.INVALID_ARGUMENT]: 'The workspace tool input was rejected.',
|
|
21
|
+
[StorageErrorCode.NOT_SUPPORTED]: 'This workspace operation is not supported by the configured storage.',
|
|
22
|
+
[StorageErrorCode.ABORTED]: 'The workspace operation was aborted.',
|
|
23
|
+
[StorageErrorCode.TIMEOUT]: 'The workspace operation timed out.',
|
|
24
|
+
[StorageErrorCode.LIMIT_EXCEEDED]: 'The workspace operation exceeded a configured limit.',
|
|
25
|
+
[StorageErrorCode.PROVIDER]: 'The workspace operation failed.',
|
|
26
|
+
};
|
|
27
|
+
/**
|
|
28
|
+
* Safe error exposed at the AI tool boundary. It deliberately carries no
|
|
29
|
+
* provider error, storage key, mount prefix, store name, or cause.
|
|
30
|
+
*/
|
|
31
|
+
export class AiSdkWorkspaceToolError extends Error {
|
|
32
|
+
code;
|
|
33
|
+
constructor(code) {
|
|
34
|
+
super(SAFE_ERROR_MESSAGES[code]);
|
|
35
|
+
this.name = 'AiSdkWorkspaceToolError';
|
|
36
|
+
this.code = code;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
const mutationToolNames = new Set([
|
|
40
|
+
'workspace_write_file',
|
|
41
|
+
'workspace_copy_file',
|
|
42
|
+
'workspace_move_file',
|
|
43
|
+
'workspace_delete_file',
|
|
44
|
+
]);
|
|
45
|
+
const utf8Encoder = new TextEncoder();
|
|
46
|
+
const forbiddenUnicodeCharacter = /\p{C}/u;
|
|
47
|
+
const workspaceCursor = /^[A-Za-z0-9_-]{32}$/u;
|
|
48
|
+
const windowsDeviceName = /^(?:con|prn|aux|nul|com[1-9]|lpt[1-9])(?:\..*)?$/iu;
|
|
49
|
+
function isLogicalPath(value) {
|
|
50
|
+
if (value.length === 0 ||
|
|
51
|
+
value.startsWith('/') ||
|
|
52
|
+
value.endsWith('/') ||
|
|
53
|
+
value !== value.normalize('NFC') ||
|
|
54
|
+
value.includes('\\') ||
|
|
55
|
+
forbiddenUnicodeCharacter.test(value)) {
|
|
56
|
+
return false;
|
|
57
|
+
}
|
|
58
|
+
return value
|
|
59
|
+
.split('/')
|
|
60
|
+
.every((segment) => segment.length > 0 &&
|
|
61
|
+
segment !== '.' &&
|
|
62
|
+
segment !== '..' &&
|
|
63
|
+
!segment.includes(':') &&
|
|
64
|
+
!segment.endsWith('.') &&
|
|
65
|
+
!segment.endsWith(' ') &&
|
|
66
|
+
!windowsDeviceName.test(segment));
|
|
67
|
+
}
|
|
68
|
+
function logicalPath(label, maxBytes) {
|
|
69
|
+
return z
|
|
70
|
+
.string()
|
|
71
|
+
.min(1)
|
|
72
|
+
.refine(isLogicalPath, {
|
|
73
|
+
message: `${label} must be a portable, NFC-normalized, relative POSIX workspace path.`,
|
|
74
|
+
})
|
|
75
|
+
.refine((value) => utf8Encoder.encode(value).byteLength <= maxBytes, {
|
|
76
|
+
message: `${label} exceeds the ${maxBytes}-byte workspace path limit.`,
|
|
77
|
+
})
|
|
78
|
+
.describe(`${label}. Use an NFC-normalized relative POSIX path inside the mounted workspace. Empty, dot, parent, colon, trailing dot or space, and Windows reserved device segments are forbidden.`);
|
|
79
|
+
}
|
|
80
|
+
function searchQuery(maxBytes) {
|
|
81
|
+
return z
|
|
82
|
+
.string()
|
|
83
|
+
.min(1)
|
|
84
|
+
.refine((value) => !value.includes('\\') && !forbiddenUnicodeCharacter.test(value), { message: 'Search query contains forbidden characters.' })
|
|
85
|
+
.refine((value) => utf8Encoder.encode(value).byteLength <= maxBytes, {
|
|
86
|
+
message: `Search query exceeds the ${maxBytes}-byte workspace limit.`,
|
|
87
|
+
})
|
|
88
|
+
.describe('Path pattern or text to find inside the mounted workspace.');
|
|
89
|
+
}
|
|
90
|
+
function boundedEtag(maxBytes) {
|
|
91
|
+
return z
|
|
92
|
+
.string()
|
|
93
|
+
.min(1)
|
|
94
|
+
.refine((value) => !forbiddenUnicodeCharacter.test(value), {
|
|
95
|
+
message: 'ETag contains forbidden characters.',
|
|
96
|
+
})
|
|
97
|
+
.refine((value) => utf8Encoder.encode(value).byteLength <= maxBytes, {
|
|
98
|
+
message: `ETag exceeds the ${maxBytes}-byte workspace limit.`,
|
|
99
|
+
})
|
|
100
|
+
.describe('Exact ETag returned by the latest workspace stat or read.');
|
|
101
|
+
}
|
|
102
|
+
function continuationCursor(description) {
|
|
103
|
+
return z
|
|
104
|
+
.string()
|
|
105
|
+
.regex(workspaceCursor, 'Cursor must be the opaque base64url token returned by the workspace.')
|
|
106
|
+
.optional()
|
|
107
|
+
.describe(description);
|
|
108
|
+
}
|
|
109
|
+
function positiveIntegerAtMost(maximum, description) {
|
|
110
|
+
return z
|
|
111
|
+
.number()
|
|
112
|
+
.int()
|
|
113
|
+
.positive()
|
|
114
|
+
.max(maximum)
|
|
115
|
+
.optional()
|
|
116
|
+
.describe(description);
|
|
117
|
+
}
|
|
118
|
+
function operationOptions(signal) {
|
|
119
|
+
return signal === undefined ? {} : { signal };
|
|
120
|
+
}
|
|
121
|
+
function resolveApproval(toolName, config) {
|
|
122
|
+
return typeof config === 'boolean' ? config : (config[toolName] ?? true);
|
|
123
|
+
}
|
|
124
|
+
function resolveReadLimit(workspace, requested) {
|
|
125
|
+
const workspaceLimit = workspace.limits.maxReadBytes;
|
|
126
|
+
if (!Number.isSafeInteger(workspaceLimit) || workspaceLimit <= 0) {
|
|
127
|
+
throw new RangeError('workspace.limits.maxReadBytes must be a positive safe integer.');
|
|
128
|
+
}
|
|
129
|
+
if (requested !== undefined &&
|
|
130
|
+
(!Number.isSafeInteger(requested) || requested <= 0)) {
|
|
131
|
+
throw new RangeError('maxReadBytes must be a positive safe integer.');
|
|
132
|
+
}
|
|
133
|
+
return Math.min(requested ?? workspaceLimit, workspaceLimit);
|
|
134
|
+
}
|
|
135
|
+
function sanitizeToolError(error, signal) {
|
|
136
|
+
if (error instanceof AiSdkWorkspaceToolError) {
|
|
137
|
+
return error;
|
|
138
|
+
}
|
|
139
|
+
if (signal?.aborted === true) {
|
|
140
|
+
return new AiSdkWorkspaceToolError(StorageErrorCode.ABORTED);
|
|
141
|
+
}
|
|
142
|
+
if (isStorageWorkspaceError(error)) {
|
|
143
|
+
return new AiSdkWorkspaceToolError(error.code);
|
|
144
|
+
}
|
|
145
|
+
if (isStorageError(error)) {
|
|
146
|
+
return new AiSdkWorkspaceToolError(error.code);
|
|
147
|
+
}
|
|
148
|
+
return new AiSdkWorkspaceToolError(StorageErrorCode.PROVIDER);
|
|
149
|
+
}
|
|
150
|
+
async function executeSafely(signal, operation) {
|
|
151
|
+
try {
|
|
152
|
+
return await operation();
|
|
153
|
+
}
|
|
154
|
+
catch (error) {
|
|
155
|
+
throw sanitizeToolError(error, signal);
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
function serializeFile(file) {
|
|
159
|
+
return {
|
|
160
|
+
kind: 'file',
|
|
161
|
+
path: file.path,
|
|
162
|
+
name: file.name,
|
|
163
|
+
size: file.size,
|
|
164
|
+
contentType: file.contentType,
|
|
165
|
+
...(file.etag === undefined ? {} : { etag: file.etag }),
|
|
166
|
+
...(file.lastModified === undefined
|
|
167
|
+
? {}
|
|
168
|
+
: { lastModified: file.lastModified.toISOString() }),
|
|
169
|
+
};
|
|
170
|
+
}
|
|
171
|
+
function serializeEntry(entry) {
|
|
172
|
+
return entry.kind === 'file'
|
|
173
|
+
? serializeFile(entry)
|
|
174
|
+
: { kind: 'directory', path: entry.path, name: entry.name };
|
|
175
|
+
}
|
|
176
|
+
function serializeTextFile(file) {
|
|
177
|
+
return { ...serializeFile(file), text: file.text };
|
|
178
|
+
}
|
|
179
|
+
function serializePage(page) {
|
|
180
|
+
return {
|
|
181
|
+
entries: page.entries.map(serializeEntry),
|
|
182
|
+
...(page.cursor === undefined ? {} : { cursor: page.cursor }),
|
|
183
|
+
};
|
|
184
|
+
}
|
|
185
|
+
/**
|
|
186
|
+
* Creates Vercel AI SDK tools backed only by a mounted StorageWorkspace.
|
|
187
|
+
*
|
|
188
|
+
* A tool is omitted unless the workspace grants its required permission.
|
|
189
|
+
* The workspace remains the enforcing boundary if a retained tool reference
|
|
190
|
+
* is invoked after further narrowing.
|
|
191
|
+
*/
|
|
192
|
+
export function createAiSdkWorkspaceTools({ workspace, maxReadBytes: requestedMaxReadBytes, requireApproval = true, }) {
|
|
193
|
+
const maxReadBytes = resolveReadLimit(workspace, requestedMaxReadBytes);
|
|
194
|
+
const tools = {};
|
|
195
|
+
const pathSchema = logicalPath('File path', workspace.limits.maxPathBytes);
|
|
196
|
+
const directorySchema = logicalPath('Directory path', workspace.limits.maxPathBytes);
|
|
197
|
+
const etagSchema = boundedEtag(workspace.limits.maxPathBytes);
|
|
198
|
+
if (workspace.allows('list')) {
|
|
199
|
+
tools.workspace_list = tool({
|
|
200
|
+
description: 'List files and directories inside the mounted workspace. Omit directory to list the workspace root.',
|
|
201
|
+
strict: true,
|
|
202
|
+
inputSchema: z
|
|
203
|
+
.object({
|
|
204
|
+
directory: directorySchema.optional(),
|
|
205
|
+
recursive: z
|
|
206
|
+
.boolean()
|
|
207
|
+
.optional()
|
|
208
|
+
.describe('Whether to include descendants recursively.'),
|
|
209
|
+
limit: positiveIntegerAtMost(workspace.limits.maxPageSize, `Maximum entries to return, up to ${workspace.limits.maxPageSize}.`),
|
|
210
|
+
cursor: continuationCursor('Opaque one-use cursor returned by the preceding list call. Repeat the same directory, recursive, and limit options when continuing.'),
|
|
211
|
+
})
|
|
212
|
+
.strict(),
|
|
213
|
+
execute: (input, { abortSignal }) => executeSafely(abortSignal, async () => serializePage(await workspace.list({
|
|
214
|
+
...(input.directory === undefined
|
|
215
|
+
? {}
|
|
216
|
+
: { directory: input.directory }),
|
|
217
|
+
...(input.recursive === undefined
|
|
218
|
+
? {}
|
|
219
|
+
: { recursive: input.recursive }),
|
|
220
|
+
...(input.limit === undefined ? {} : { limit: input.limit }),
|
|
221
|
+
...(input.cursor === undefined ? {} : { cursor: input.cursor }),
|
|
222
|
+
...operationOptions(abortSignal),
|
|
223
|
+
}))),
|
|
224
|
+
});
|
|
225
|
+
}
|
|
226
|
+
if (workspace.allows('read')) {
|
|
227
|
+
tools.workspace_stat = tool({
|
|
228
|
+
description: 'Inspect a file inside the mounted workspace without reading its contents. Returns the ETag required for safe replace, move, and delete operations.',
|
|
229
|
+
strict: true,
|
|
230
|
+
inputSchema: z.object({ path: pathSchema }).strict(),
|
|
231
|
+
execute: ({ path }, { abortSignal }) => executeSafely(abortSignal, async () => serializeFile(await workspace.stat(path, operationOptions(abortSignal)))),
|
|
232
|
+
});
|
|
233
|
+
tools.workspace_read_file = tool({
|
|
234
|
+
description: `Read a UTF-8 text file inside the mounted workspace. The result is bounded to ${maxReadBytes} bytes.`,
|
|
235
|
+
strict: true,
|
|
236
|
+
inputSchema: z.object({ path: pathSchema }).strict(),
|
|
237
|
+
execute: ({ path }, { abortSignal }) => executeSafely(abortSignal, async () => serializeTextFile(await workspace.readText(path, {
|
|
238
|
+
maxBytes: maxReadBytes,
|
|
239
|
+
...operationOptions(abortSignal),
|
|
240
|
+
}))),
|
|
241
|
+
});
|
|
242
|
+
}
|
|
243
|
+
if (workspace.allows('search')) {
|
|
244
|
+
tools.workspace_search = tool({
|
|
245
|
+
description: 'Search logical paths inside the mounted workspace using a bounded glob, substring, or exact match. Omit directory to search from the workspace root.',
|
|
246
|
+
strict: true,
|
|
247
|
+
inputSchema: z
|
|
248
|
+
.object({
|
|
249
|
+
query: searchQuery(workspace.limits.maxPathBytes),
|
|
250
|
+
directory: directorySchema.optional(),
|
|
251
|
+
match: z
|
|
252
|
+
.enum(['glob', 'substring', 'exact'])
|
|
253
|
+
.optional()
|
|
254
|
+
.describe('Matching strategy; defaults to glob.'),
|
|
255
|
+
caseInsensitive: z.boolean().optional(),
|
|
256
|
+
limit: positiveIntegerAtMost(workspace.limits.maxSearchResults, `Maximum matches to return, up to ${workspace.limits.maxSearchResults}.`),
|
|
257
|
+
cursor: continuationCursor('Opaque one-use cursor returned by the preceding search call. Repeat the same query, directory, match, caseInsensitive, and limit options when continuing.'),
|
|
258
|
+
})
|
|
259
|
+
.strict(),
|
|
260
|
+
execute: ({ query, directory, match, caseInsensitive, limit, cursor }, { abortSignal }) => executeSafely(abortSignal, async () => serializePage(await workspace.search(query, {
|
|
261
|
+
...(directory === undefined ? {} : { directory }),
|
|
262
|
+
...(match === undefined ? {} : { match }),
|
|
263
|
+
...(caseInsensitive === undefined ? {} : { caseInsensitive }),
|
|
264
|
+
...(limit === undefined ? {} : { limit }),
|
|
265
|
+
...(cursor === undefined ? {} : { cursor }),
|
|
266
|
+
...operationOptions(abortSignal),
|
|
267
|
+
}))),
|
|
268
|
+
});
|
|
269
|
+
}
|
|
270
|
+
const canCreate = workspace.allows('create');
|
|
271
|
+
const canReplace = workspace.allows('replace');
|
|
272
|
+
if (canCreate || canReplace) {
|
|
273
|
+
const commonWriteShape = {
|
|
274
|
+
path: pathSchema,
|
|
275
|
+
content: z
|
|
276
|
+
.string()
|
|
277
|
+
.refine((value) => utf8Encoder.encode(value).byteLength <=
|
|
278
|
+
workspace.limits.maxWriteBytes, {
|
|
279
|
+
message: `Content exceeds the ${workspace.limits.maxWriteBytes}-byte workspace write limit.`,
|
|
280
|
+
})
|
|
281
|
+
.describe(`UTF-8 text to write. The workspace enforces its ${workspace.limits.maxWriteBytes}-byte write limit.`),
|
|
282
|
+
};
|
|
283
|
+
const createSchema = z
|
|
284
|
+
.object({ ...commonWriteShape, mode: z.literal('create') })
|
|
285
|
+
.strict();
|
|
286
|
+
const replaceSchema = z
|
|
287
|
+
.object({
|
|
288
|
+
...commonWriteShape,
|
|
289
|
+
mode: z.literal('replace'),
|
|
290
|
+
etag: etagSchema,
|
|
291
|
+
})
|
|
292
|
+
.strict();
|
|
293
|
+
const inputSchema = canCreate && canReplace
|
|
294
|
+
? z.discriminatedUnion('mode', [createSchema, replaceSchema])
|
|
295
|
+
: canCreate
|
|
296
|
+
? createSchema
|
|
297
|
+
: replaceSchema;
|
|
298
|
+
tools.workspace_write_file = tool({
|
|
299
|
+
description: canCreate && canReplace
|
|
300
|
+
? 'Create a new UTF-8 text file or replace an existing file inside the mounted workspace. Create fails if the destination exists; replace requires its current ETag.'
|
|
301
|
+
: canCreate
|
|
302
|
+
? 'Create a new UTF-8 text file inside the mounted workspace. The operation fails if the destination already exists.'
|
|
303
|
+
: 'Replace an existing UTF-8 text file inside the mounted workspace using its current ETag.',
|
|
304
|
+
strict: true,
|
|
305
|
+
inputSchema,
|
|
306
|
+
needsApproval: resolveApproval('workspace_write_file', requireApproval),
|
|
307
|
+
execute: (input, { abortSignal }) => executeSafely(abortSignal, async () => serializeFile(input.mode === 'create'
|
|
308
|
+
? await workspace.writeFile(input.path, input.content, {
|
|
309
|
+
mode: 'create',
|
|
310
|
+
...operationOptions(abortSignal),
|
|
311
|
+
})
|
|
312
|
+
: await workspace.writeFile(input.path, input.content, {
|
|
313
|
+
mode: 'replace',
|
|
314
|
+
etag: input.etag,
|
|
315
|
+
...operationOptions(abortSignal),
|
|
316
|
+
}))),
|
|
317
|
+
});
|
|
318
|
+
}
|
|
319
|
+
const canCopy = workspace.allows('copy') &&
|
|
320
|
+
workspace.allows('read') &&
|
|
321
|
+
workspace.allows('create');
|
|
322
|
+
if (canCopy) {
|
|
323
|
+
tools.workspace_copy_file = tool({
|
|
324
|
+
description: 'Copy a file inside the mounted workspace. The source remains intact, and the operation fails if the destination already exists.',
|
|
325
|
+
strict: true,
|
|
326
|
+
inputSchema: z
|
|
327
|
+
.object({
|
|
328
|
+
source: logicalPath('Source file path', workspace.limits.maxPathBytes),
|
|
329
|
+
destination: logicalPath('Destination file path', workspace.limits.maxPathBytes),
|
|
330
|
+
})
|
|
331
|
+
.strict(),
|
|
332
|
+
needsApproval: resolveApproval('workspace_copy_file', requireApproval),
|
|
333
|
+
execute: ({ source, destination }, { abortSignal }) => executeSafely(abortSignal, async () => serializeFile(await workspace.copyFile(source, destination, operationOptions(abortSignal)))),
|
|
334
|
+
});
|
|
335
|
+
}
|
|
336
|
+
const canMove = workspace.allows('move') &&
|
|
337
|
+
workspace.allows('read') &&
|
|
338
|
+
workspace.allows('create') &&
|
|
339
|
+
workspace.allows('delete');
|
|
340
|
+
if (canMove) {
|
|
341
|
+
tools.workspace_move_file = tool({
|
|
342
|
+
description: "Move a file inside the mounted workspace using the source's current ETag. The operation fails if the destination already exists. If source deletion cannot be confirmed, the destination is retained and the tool reports a conflict; inspect both paths before retrying.",
|
|
343
|
+
strict: true,
|
|
344
|
+
inputSchema: z
|
|
345
|
+
.object({
|
|
346
|
+
source: logicalPath('Source file path', workspace.limits.maxPathBytes),
|
|
347
|
+
destination: logicalPath('Destination file path', workspace.limits.maxPathBytes),
|
|
348
|
+
etag: etagSchema.describe('Exact ETag of the source returned by the latest workspace stat or read.'),
|
|
349
|
+
})
|
|
350
|
+
.strict(),
|
|
351
|
+
needsApproval: resolveApproval('workspace_move_file', requireApproval),
|
|
352
|
+
execute: ({ source, destination, etag }, { abortSignal }) => executeSafely(abortSignal, async () => serializeFile(await workspace.moveFile(source, destination, {
|
|
353
|
+
etag,
|
|
354
|
+
...operationOptions(abortSignal),
|
|
355
|
+
}))),
|
|
356
|
+
});
|
|
357
|
+
}
|
|
358
|
+
if (workspace.allows('delete')) {
|
|
359
|
+
tools.workspace_delete_file = tool({
|
|
360
|
+
description: 'Delete a file inside the mounted workspace using its current ETag.',
|
|
361
|
+
strict: true,
|
|
362
|
+
inputSchema: z
|
|
363
|
+
.object({
|
|
364
|
+
path: pathSchema,
|
|
365
|
+
etag: etagSchema,
|
|
366
|
+
})
|
|
367
|
+
.strict(),
|
|
368
|
+
needsApproval: resolveApproval('workspace_delete_file', requireApproval),
|
|
369
|
+
execute: ({ path, etag }, { abortSignal }) => executeSafely(abortSignal, async () => {
|
|
370
|
+
await workspace.deleteFile(path, {
|
|
371
|
+
etag,
|
|
372
|
+
...operationOptions(abortSignal),
|
|
373
|
+
});
|
|
374
|
+
return { deleted: true, path };
|
|
375
|
+
}),
|
|
376
|
+
});
|
|
377
|
+
}
|
|
378
|
+
return tools;
|
|
379
|
+
}
|
|
380
|
+
export function isAiSdkWorkspaceMutationToolName(value) {
|
|
381
|
+
return mutationToolNames.has(value);
|
|
382
|
+
}
|
|
383
|
+
//# sourceMappingURL=ai-sdk-workspace-tools.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ai-sdk-workspace-tools.js","sourceRoot":"","sources":["../../src/ai-sdk/ai-sdk-workspace-tools.ts"],"names":[],"mappings":"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","sourcesContent":["import { tool, type ToolSet } from 'ai';\nimport { z } from 'zod';\n\nimport {\n isStorageWorkspaceError,\n type StorageWorkspace,\n type StorageWorkspaceEntry,\n type StorageWorkspaceFile,\n type StorageWorkspaceTextFile,\n} from '../workspace/index.js';\nimport {\n StorageErrorCode,\n isStorageError,\n type StorageErrorCode as StorageErrorCodeValue,\n} from '../storage.error.js';\n\nexport const AI_SDK_WORKSPACE_TOOL_NAMES = [\n 'workspace_list',\n 'workspace_stat',\n 'workspace_read_file',\n 'workspace_search',\n 'workspace_write_file',\n 'workspace_copy_file',\n 'workspace_move_file',\n 'workspace_delete_file',\n] as const;\n\nexport type AiSdkWorkspaceToolName =\n (typeof AI_SDK_WORKSPACE_TOOL_NAMES)[number];\n\nexport type AiSdkWorkspaceMutationToolName = Extract<\n AiSdkWorkspaceToolName,\n | 'workspace_write_file'\n | 'workspace_copy_file'\n | 'workspace_move_file'\n | 'workspace_delete_file'\n>;\n\n/**\n * Approval policy for mutation tools. An omitted entry defaults to requiring\n * approval, matching an omitted policy.\n */\nexport type AiSdkWorkspaceApprovalConfig =\n boolean | Partial<Record<AiSdkWorkspaceMutationToolName, boolean>>;\n\nexport interface CreateAiSdkWorkspaceToolsOptions {\n /** The already-mounted, policy-enforcing workspace exposed to the tools. */\n workspace: StorageWorkspace;\n /**\n * Optional tighter read ceiling. Values above the workspace ceiling are\n * clamped; the model cannot choose or raise this value.\n */\n maxReadBytes?: number;\n /** Mutation tools require approval by default. */\n requireApproval?: AiSdkWorkspaceApprovalConfig;\n}\n\nexport type AiSdkWorkspaceToolErrorCode = StorageErrorCodeValue;\n\nconst SAFE_ERROR_MESSAGES: Readonly<\n Record<AiSdkWorkspaceToolErrorCode, string>\n> = {\n [StorageErrorCode.NOT_FOUND]: 'The requested workspace path was not found.',\n [StorageErrorCode.UNAUTHORIZED]:\n 'This operation is not permitted in the workspace.',\n [StorageErrorCode.CONFLICT]:\n 'The operation conflicts with current workspace state. Refresh metadata and retry with the current ETag or a new destination.',\n [StorageErrorCode.READ_ONLY]:\n 'This operation is not permitted in the workspace.',\n [StorageErrorCode.INVALID_ARGUMENT]: 'The workspace tool input was rejected.',\n [StorageErrorCode.NOT_SUPPORTED]:\n 'This workspace operation is not supported by the configured storage.',\n [StorageErrorCode.ABORTED]: 'The workspace operation was aborted.',\n [StorageErrorCode.TIMEOUT]: 'The workspace operation timed out.',\n [StorageErrorCode.LIMIT_EXCEEDED]:\n 'The workspace operation exceeded a configured limit.',\n [StorageErrorCode.PROVIDER]: 'The workspace operation failed.',\n};\n\n/**\n * Safe error exposed at the AI tool boundary. It deliberately carries no\n * provider error, storage key, mount prefix, store name, or cause.\n */\nexport class AiSdkWorkspaceToolError extends Error {\n readonly code: AiSdkWorkspaceToolErrorCode;\n\n constructor(code: AiSdkWorkspaceToolErrorCode) {\n super(SAFE_ERROR_MESSAGES[code]);\n this.name = 'AiSdkWorkspaceToolError';\n this.code = code;\n }\n}\n\nexport interface AiSdkWorkspaceFileResult {\n kind: 'file';\n path: string;\n name: string;\n size: number;\n contentType: string;\n etag?: string;\n lastModified?: string;\n}\n\nexport interface AiSdkWorkspaceDirectoryResult {\n kind: 'directory';\n path: string;\n name: string;\n}\n\nexport type AiSdkWorkspaceEntryResult =\n AiSdkWorkspaceFileResult | AiSdkWorkspaceDirectoryResult;\n\nexport interface AiSdkWorkspaceTextFileResult extends AiSdkWorkspaceFileResult {\n text: string;\n}\n\nexport interface AiSdkWorkspacePageResult {\n entries: AiSdkWorkspaceEntryResult[];\n cursor?: string;\n}\n\nconst mutationToolNames = new Set<AiSdkWorkspaceMutationToolName>([\n 'workspace_write_file',\n 'workspace_copy_file',\n 'workspace_move_file',\n 'workspace_delete_file',\n]);\nconst utf8Encoder = new TextEncoder();\nconst forbiddenUnicodeCharacter = /\\p{C}/u;\nconst workspaceCursor = /^[A-Za-z0-9_-]{32}$/u;\nconst windowsDeviceName = /^(?:con|prn|aux|nul|com[1-9]|lpt[1-9])(?:\\..*)?$/iu;\n\nfunction isLogicalPath(value: string): boolean {\n if (\n value.length === 0 ||\n value.startsWith('/') ||\n value.endsWith('/') ||\n value !== value.normalize('NFC') ||\n value.includes('\\\\') ||\n forbiddenUnicodeCharacter.test(value)\n ) {\n return false;\n }\n\n return value\n .split('/')\n .every(\n (segment) =>\n segment.length > 0 &&\n segment !== '.' &&\n segment !== '..' &&\n !segment.includes(':') &&\n !segment.endsWith('.') &&\n !segment.endsWith(' ') &&\n !windowsDeviceName.test(segment),\n );\n}\n\nfunction logicalPath(label: string, maxBytes: number) {\n return z\n .string()\n .min(1)\n .refine(isLogicalPath, {\n message: `${label} must be a portable, NFC-normalized, relative POSIX workspace path.`,\n })\n .refine((value) => utf8Encoder.encode(value).byteLength <= maxBytes, {\n message: `${label} exceeds the ${maxBytes}-byte workspace path limit.`,\n })\n .describe(\n `${label}. Use an NFC-normalized relative POSIX path inside the mounted workspace. Empty, dot, parent, colon, trailing dot or space, and Windows reserved device segments are forbidden.`,\n );\n}\n\nfunction searchQuery(maxBytes: number) {\n return z\n .string()\n .min(1)\n .refine(\n (value) =>\n !value.includes('\\\\') && !forbiddenUnicodeCharacter.test(value),\n { message: 'Search query contains forbidden characters.' },\n )\n .refine((value) => utf8Encoder.encode(value).byteLength <= maxBytes, {\n message: `Search query exceeds the ${maxBytes}-byte workspace limit.`,\n })\n .describe('Path pattern or text to find inside the mounted workspace.');\n}\n\nfunction boundedEtag(maxBytes: number) {\n return z\n .string()\n .min(1)\n .refine((value) => !forbiddenUnicodeCharacter.test(value), {\n message: 'ETag contains forbidden characters.',\n })\n .refine((value) => utf8Encoder.encode(value).byteLength <= maxBytes, {\n message: `ETag exceeds the ${maxBytes}-byte workspace limit.`,\n })\n .describe('Exact ETag returned by the latest workspace stat or read.');\n}\n\nfunction continuationCursor(description: string) {\n return z\n .string()\n .regex(\n workspaceCursor,\n 'Cursor must be the opaque base64url token returned by the workspace.',\n )\n .optional()\n .describe(description);\n}\n\nfunction positiveIntegerAtMost(maximum: number, description: string) {\n return z\n .number()\n .int()\n .positive()\n .max(maximum)\n .optional()\n .describe(description);\n}\n\nfunction operationOptions(signal: AbortSignal | undefined): {\n signal?: AbortSignal;\n} {\n return signal === undefined ? {} : { signal };\n}\n\nfunction resolveApproval(\n toolName: AiSdkWorkspaceMutationToolName,\n config: AiSdkWorkspaceApprovalConfig,\n): boolean {\n return typeof config === 'boolean' ? config : (config[toolName] ?? true);\n}\n\nfunction resolveReadLimit(\n workspace: StorageWorkspace,\n requested: number | undefined,\n): number {\n const workspaceLimit = workspace.limits.maxReadBytes;\n if (!Number.isSafeInteger(workspaceLimit) || workspaceLimit <= 0) {\n throw new RangeError(\n 'workspace.limits.maxReadBytes must be a positive safe integer.',\n );\n }\n if (\n requested !== undefined &&\n (!Number.isSafeInteger(requested) || requested <= 0)\n ) {\n throw new RangeError('maxReadBytes must be a positive safe integer.');\n }\n return Math.min(requested ?? workspaceLimit, workspaceLimit);\n}\n\nfunction sanitizeToolError(\n error: unknown,\n signal: AbortSignal | undefined,\n): AiSdkWorkspaceToolError {\n if (error instanceof AiSdkWorkspaceToolError) {\n return error;\n }\n if (signal?.aborted === true) {\n return new AiSdkWorkspaceToolError(StorageErrorCode.ABORTED);\n }\n if (isStorageWorkspaceError(error)) {\n return new AiSdkWorkspaceToolError(error.code);\n }\n if (isStorageError(error)) {\n return new AiSdkWorkspaceToolError(error.code);\n }\n return new AiSdkWorkspaceToolError(StorageErrorCode.PROVIDER);\n}\n\nasync function executeSafely<Result>(\n signal: AbortSignal | undefined,\n operation: () => Promise<Result>,\n): Promise<Result> {\n try {\n return await operation();\n } catch (error) {\n throw sanitizeToolError(error, signal);\n }\n}\n\nfunction serializeFile(file: StorageWorkspaceFile): AiSdkWorkspaceFileResult {\n return {\n kind: 'file',\n path: file.path,\n name: file.name,\n size: file.size,\n contentType: file.contentType,\n ...(file.etag === undefined ? {} : { etag: file.etag }),\n ...(file.lastModified === undefined\n ? {}\n : { lastModified: file.lastModified.toISOString() }),\n };\n}\n\nfunction serializeEntry(\n entry: StorageWorkspaceEntry,\n): AiSdkWorkspaceEntryResult {\n return entry.kind === 'file'\n ? serializeFile(entry)\n : { kind: 'directory', path: entry.path, name: entry.name };\n}\n\nfunction serializeTextFile(\n file: StorageWorkspaceTextFile,\n): AiSdkWorkspaceTextFileResult {\n return { ...serializeFile(file), text: file.text };\n}\n\nfunction serializePage(page: {\n entries: StorageWorkspaceEntry[];\n cursor?: string;\n}): AiSdkWorkspacePageResult {\n return {\n entries: page.entries.map(serializeEntry),\n ...(page.cursor === undefined ? {} : { cursor: page.cursor }),\n };\n}\n\n/**\n * Creates Vercel AI SDK tools backed only by a mounted StorageWorkspace.\n *\n * A tool is omitted unless the workspace grants its required permission.\n * The workspace remains the enforcing boundary if a retained tool reference\n * is invoked after further narrowing.\n */\nexport function createAiSdkWorkspaceTools({\n workspace,\n maxReadBytes: requestedMaxReadBytes,\n requireApproval = true,\n}: CreateAiSdkWorkspaceToolsOptions): ToolSet {\n const maxReadBytes = resolveReadLimit(workspace, requestedMaxReadBytes);\n const tools: ToolSet = {};\n const pathSchema = logicalPath('File path', workspace.limits.maxPathBytes);\n const directorySchema = logicalPath(\n 'Directory path',\n workspace.limits.maxPathBytes,\n );\n const etagSchema = boundedEtag(workspace.limits.maxPathBytes);\n\n if (workspace.allows('list')) {\n tools.workspace_list = tool({\n description:\n 'List files and directories inside the mounted workspace. Omit directory to list the workspace root.',\n strict: true,\n inputSchema: z\n .object({\n directory: directorySchema.optional(),\n recursive: z\n .boolean()\n .optional()\n .describe('Whether to include descendants recursively.'),\n limit: positiveIntegerAtMost(\n workspace.limits.maxPageSize,\n `Maximum entries to return, up to ${workspace.limits.maxPageSize}.`,\n ),\n cursor: continuationCursor(\n 'Opaque one-use cursor returned by the preceding list call. Repeat the same directory, recursive, and limit options when continuing.',\n ),\n })\n .strict(),\n execute: (input, { abortSignal }) =>\n executeSafely(abortSignal, async () =>\n serializePage(\n await workspace.list({\n ...(input.directory === undefined\n ? {}\n : { directory: input.directory }),\n ...(input.recursive === undefined\n ? {}\n : { recursive: input.recursive }),\n ...(input.limit === undefined ? {} : { limit: input.limit }),\n ...(input.cursor === undefined ? {} : { cursor: input.cursor }),\n ...operationOptions(abortSignal),\n }),\n ),\n ),\n });\n }\n\n if (workspace.allows('read')) {\n tools.workspace_stat = tool({\n description:\n 'Inspect a file inside the mounted workspace without reading its contents. Returns the ETag required for safe replace, move, and delete operations.',\n strict: true,\n inputSchema: z.object({ path: pathSchema }).strict(),\n execute: ({ path }, { abortSignal }) =>\n executeSafely(abortSignal, async () =>\n serializeFile(\n await workspace.stat(path, operationOptions(abortSignal)),\n ),\n ),\n });\n\n tools.workspace_read_file = tool({\n description: `Read a UTF-8 text file inside the mounted workspace. The result is bounded to ${maxReadBytes} bytes.`,\n strict: true,\n inputSchema: z.object({ path: pathSchema }).strict(),\n execute: ({ path }, { abortSignal }) =>\n executeSafely(abortSignal, async () =>\n serializeTextFile(\n await workspace.readText(path, {\n maxBytes: maxReadBytes,\n ...operationOptions(abortSignal),\n }),\n ),\n ),\n });\n }\n\n if (workspace.allows('search')) {\n tools.workspace_search = tool({\n description:\n 'Search logical paths inside the mounted workspace using a bounded glob, substring, or exact match. Omit directory to search from the workspace root.',\n strict: true,\n inputSchema: z\n .object({\n query: searchQuery(workspace.limits.maxPathBytes),\n directory: directorySchema.optional(),\n match: z\n .enum(['glob', 'substring', 'exact'])\n .optional()\n .describe('Matching strategy; defaults to glob.'),\n caseInsensitive: z.boolean().optional(),\n limit: positiveIntegerAtMost(\n workspace.limits.maxSearchResults,\n `Maximum matches to return, up to ${workspace.limits.maxSearchResults}.`,\n ),\n cursor: continuationCursor(\n 'Opaque one-use cursor returned by the preceding search call. Repeat the same query, directory, match, caseInsensitive, and limit options when continuing.',\n ),\n })\n .strict(),\n execute: (\n { query, directory, match, caseInsensitive, limit, cursor },\n { abortSignal },\n ) =>\n executeSafely(abortSignal, async () =>\n serializePage(\n await workspace.search(query, {\n ...(directory === undefined ? {} : { directory }),\n ...(match === undefined ? {} : { match }),\n ...(caseInsensitive === undefined ? {} : { caseInsensitive }),\n ...(limit === undefined ? {} : { limit }),\n ...(cursor === undefined ? {} : { cursor }),\n ...operationOptions(abortSignal),\n }),\n ),\n ),\n });\n }\n\n const canCreate = workspace.allows('create');\n const canReplace = workspace.allows('replace');\n if (canCreate || canReplace) {\n const commonWriteShape = {\n path: pathSchema,\n content: z\n .string()\n .refine(\n (value) =>\n utf8Encoder.encode(value).byteLength <=\n workspace.limits.maxWriteBytes,\n {\n message: `Content exceeds the ${workspace.limits.maxWriteBytes}-byte workspace write limit.`,\n },\n )\n .describe(\n `UTF-8 text to write. The workspace enforces its ${workspace.limits.maxWriteBytes}-byte write limit.`,\n ),\n };\n const createSchema = z\n .object({ ...commonWriteShape, mode: z.literal('create') })\n .strict();\n const replaceSchema = z\n .object({\n ...commonWriteShape,\n mode: z.literal('replace'),\n etag: etagSchema,\n })\n .strict();\n const inputSchema =\n canCreate && canReplace\n ? z.discriminatedUnion('mode', [createSchema, replaceSchema])\n : canCreate\n ? createSchema\n : replaceSchema;\n\n tools.workspace_write_file = tool({\n description:\n canCreate && canReplace\n ? 'Create a new UTF-8 text file or replace an existing file inside the mounted workspace. Create fails if the destination exists; replace requires its current ETag.'\n : canCreate\n ? 'Create a new UTF-8 text file inside the mounted workspace. The operation fails if the destination already exists.'\n : 'Replace an existing UTF-8 text file inside the mounted workspace using its current ETag.',\n strict: true,\n inputSchema,\n needsApproval: resolveApproval('workspace_write_file', requireApproval),\n execute: (input, { abortSignal }) =>\n executeSafely(abortSignal, async () =>\n serializeFile(\n input.mode === 'create'\n ? await workspace.writeFile(input.path, input.content, {\n mode: 'create',\n ...operationOptions(abortSignal),\n })\n : await workspace.writeFile(input.path, input.content, {\n mode: 'replace',\n etag: input.etag,\n ...operationOptions(abortSignal),\n }),\n ),\n ),\n });\n }\n\n const canCopy =\n workspace.allows('copy') &&\n workspace.allows('read') &&\n workspace.allows('create');\n if (canCopy) {\n tools.workspace_copy_file = tool({\n description:\n 'Copy a file inside the mounted workspace. The source remains intact, and the operation fails if the destination already exists.',\n strict: true,\n inputSchema: z\n .object({\n source: logicalPath(\n 'Source file path',\n workspace.limits.maxPathBytes,\n ),\n destination: logicalPath(\n 'Destination file path',\n workspace.limits.maxPathBytes,\n ),\n })\n .strict(),\n needsApproval: resolveApproval('workspace_copy_file', requireApproval),\n execute: ({ source, destination }, { abortSignal }) =>\n executeSafely(abortSignal, async () =>\n serializeFile(\n await workspace.copyFile(\n source,\n destination,\n operationOptions(abortSignal),\n ),\n ),\n ),\n });\n }\n\n const canMove =\n workspace.allows('move') &&\n workspace.allows('read') &&\n workspace.allows('create') &&\n workspace.allows('delete');\n if (canMove) {\n tools.workspace_move_file = tool({\n description:\n \"Move a file inside the mounted workspace using the source's current ETag. The operation fails if the destination already exists. If source deletion cannot be confirmed, the destination is retained and the tool reports a conflict; inspect both paths before retrying.\",\n strict: true,\n inputSchema: z\n .object({\n source: logicalPath(\n 'Source file path',\n workspace.limits.maxPathBytes,\n ),\n destination: logicalPath(\n 'Destination file path',\n workspace.limits.maxPathBytes,\n ),\n etag: etagSchema.describe(\n 'Exact ETag of the source returned by the latest workspace stat or read.',\n ),\n })\n .strict(),\n needsApproval: resolveApproval('workspace_move_file', requireApproval),\n execute: ({ source, destination, etag }, { abortSignal }) =>\n executeSafely(abortSignal, async () =>\n serializeFile(\n await workspace.moveFile(source, destination, {\n etag,\n ...operationOptions(abortSignal),\n }),\n ),\n ),\n });\n }\n\n if (workspace.allows('delete')) {\n tools.workspace_delete_file = tool({\n description:\n 'Delete a file inside the mounted workspace using its current ETag.',\n strict: true,\n inputSchema: z\n .object({\n path: pathSchema,\n etag: etagSchema,\n })\n .strict(),\n needsApproval: resolveApproval('workspace_delete_file', requireApproval),\n execute: ({ path, etag }, { abortSignal }) =>\n executeSafely(abortSignal, async () => {\n await workspace.deleteFile(path, {\n etag,\n ...operationOptions(abortSignal),\n });\n return { deleted: true as const, path };\n }),\n });\n }\n\n return tools;\n}\n\nexport function isAiSdkWorkspaceMutationToolName(\n value: string,\n): value is AiSdkWorkspaceMutationToolName {\n return mutationToolNames.has(value as AiSdkWorkspaceMutationToolName);\n}\n"]}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
export { AI_SDK_WORKSPACE_TOOL_NAMES, AiSdkWorkspaceToolError, createAiSdkWorkspaceTools, isAiSdkWorkspaceMutationToolName, type AiSdkWorkspaceApprovalConfig, type AiSdkWorkspaceDirectoryResult, type AiSdkWorkspaceEntryResult, type AiSdkWorkspaceFileResult, type AiSdkWorkspaceMutationToolName, type AiSdkWorkspacePageResult, type AiSdkWorkspaceTextFileResult, type AiSdkWorkspaceToolErrorCode, type AiSdkWorkspaceToolName, type CreateAiSdkWorkspaceToolsOptions, } from './ai-sdk-workspace-tools.js';
|
|
2
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/ai-sdk/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,2BAA2B,EAC3B,uBAAuB,EACvB,yBAAyB,EACzB,gCAAgC,EAChC,KAAK,4BAA4B,EACjC,KAAK,6BAA6B,EAClC,KAAK,yBAAyB,EAC9B,KAAK,wBAAwB,EAC7B,KAAK,8BAA8B,EACnC,KAAK,wBAAwB,EAC7B,KAAK,4BAA4B,EACjC,KAAK,2BAA2B,EAChC,KAAK,sBAAsB,EAC3B,KAAK,gCAAgC,GACtC,MAAM,6BAA6B,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/ai-sdk/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,2BAA2B,EAC3B,uBAAuB,EACvB,yBAAyB,EACzB,gCAAgC,GAWjC,MAAM,6BAA6B,CAAC","sourcesContent":["export {\n AI_SDK_WORKSPACE_TOOL_NAMES,\n AiSdkWorkspaceToolError,\n createAiSdkWorkspaceTools,\n isAiSdkWorkspaceMutationToolName,\n type AiSdkWorkspaceApprovalConfig,\n type AiSdkWorkspaceDirectoryResult,\n type AiSdkWorkspaceEntryResult,\n type AiSdkWorkspaceFileResult,\n type AiSdkWorkspaceMutationToolName,\n type AiSdkWorkspacePageResult,\n type AiSdkWorkspaceTextFileResult,\n type AiSdkWorkspaceToolErrorCode,\n type AiSdkWorkspaceToolName,\n type CreateAiSdkWorkspaceToolsOptions,\n} from './ai-sdk-workspace-tools.js';\n"]}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { type Adapter, type FilesOptions } from 'files-sdk';
|
|
1
|
+
import { type Adapter, type Body, type FilesOptions } from 'files-sdk';
|
|
2
2
|
import { StorageError } from '../storage.error.js';
|
|
3
3
|
import type { StorageDriver } from '../storage.driver.js';
|
|
4
|
-
import type { StorageBody, StorageDownloadOptions, StorageListOptions, StorageListResult, StorageObject, StorageObjectMetadata, StorageOperationOptions, StorageConditionalCopyCapability, StoragePromotionOptions, StorageSearchOptions, StorageSignedUploadPolicyCapability, StorageSignedDownloadPolicyCapability, StorageSignedDownloadOptions, StorageSignedUpload, StorageSignedUploadOptions, StorageUploadOptions, StorageUploadResult } from '../storage.types.js';
|
|
4
|
+
import type { StorageBody, StorageConditionalDeleteOptions, StorageConditionalMutationCapability, StorageConditionalUploadOptions, StorageDownloadOptions, StorageListOptions, StorageListResult, StorageObject, StorageObjectMetadata, StorageOperationOptions, StorageConditionalCopyCapability, StoragePromotionOptions, StorageSearchOptions, StorageSignedUploadPolicyCapability, StorageSignedDownloadPolicyCapability, StorageSignedDownloadOptions, StorageSignedUpload, StorageSignedUploadOptions, StorageUploadOptions, StorageUploadResult } from '../storage.types.js';
|
|
5
5
|
export type FilesSdkDriverOptions<AdapterType extends Adapter> = FilesOptions<AdapterType>;
|
|
6
6
|
/**
|
|
7
7
|
* Optional adapter extension for providers that can conditionally copy an
|
|
@@ -11,6 +11,12 @@ export interface FilesSdkConditionalCopyAdapter {
|
|
|
11
11
|
readonly conditionalCopy: StorageConditionalCopyCapability;
|
|
12
12
|
promote(sourceKey: string, destinationKey: string, options: StoragePromotionOptions): Promise<void>;
|
|
13
13
|
}
|
|
14
|
+
/** Optional adapter extension for native compare-and-set mutations. */
|
|
15
|
+
export interface FilesSdkConditionalMutationAdapter {
|
|
16
|
+
readonly conditionalMutation: StorageConditionalMutationCapability;
|
|
17
|
+
uploadConditional(key: string, body: Body, options: StorageConditionalUploadOptions): Promise<StorageUploadResult>;
|
|
18
|
+
deleteConditional(key: string, options: StorageConditionalDeleteOptions): Promise<void>;
|
|
19
|
+
}
|
|
14
20
|
export interface FilesSdkSignedUploadPolicyAdapter {
|
|
15
21
|
readonly signedUploadPolicy: StorageSignedUploadPolicyCapability;
|
|
16
22
|
}
|
|
@@ -34,6 +40,12 @@ export declare class FilesSdkStorageDriver<AdapterType extends Adapter = Adapter
|
|
|
34
40
|
etag: boolean;
|
|
35
41
|
version: boolean;
|
|
36
42
|
};
|
|
43
|
+
conditionalMutation?: {
|
|
44
|
+
create: boolean;
|
|
45
|
+
replace: boolean;
|
|
46
|
+
delete: boolean;
|
|
47
|
+
etag: boolean;
|
|
48
|
+
};
|
|
37
49
|
signedDownload: {
|
|
38
50
|
supported: boolean;
|
|
39
51
|
maxExpiresIn?: number;
|
|
@@ -49,10 +61,12 @@ export declare class FilesSdkStorageDriver<AdapterType extends Adapter = Adapter
|
|
|
49
61
|
nativeUploadProgress: boolean;
|
|
50
62
|
};
|
|
51
63
|
upload(key: string, body: StorageBody, options?: StorageUploadOptions): Promise<StorageUploadResult>;
|
|
64
|
+
uploadConditional(key: string, body: StorageBody, options: StorageConditionalUploadOptions): Promise<StorageUploadResult>;
|
|
52
65
|
download(key: string, options?: StorageDownloadOptions): Promise<StorageObject>;
|
|
53
66
|
head(key: string, options?: StorageOperationOptions): Promise<StorageObjectMetadata>;
|
|
54
67
|
exists(key: string, options?: StorageOperationOptions): Promise<boolean>;
|
|
55
68
|
delete(key: string, options?: StorageOperationOptions): Promise<void>;
|
|
69
|
+
deleteConditional(key: string, options: StorageConditionalDeleteOptions): Promise<void>;
|
|
56
70
|
copy(sourceKey: string, destinationKey: string, options?: StorageOperationOptions): Promise<void>;
|
|
57
71
|
move(sourceKey: string, destinationKey: string, options?: StorageOperationOptions): Promise<void>;
|
|
58
72
|
promote(sourceKey: string, destinationKey: string, options: StoragePromotionOptions): Promise<void>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"files-sdk.driver.d.ts","sourceRoot":"","sources":["../../src/files-sdk/files-sdk.driver.ts"],"names":[],"mappings":"AAEA,OAAO,EAGL,KAAK,OAAO,
|
|
1
|
+
{"version":3,"file":"files-sdk.driver.d.ts","sourceRoot":"","sources":["../../src/files-sdk/files-sdk.driver.ts"],"names":[],"mappings":"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"}
|