@dbx-tools/appkit-mastra 0.9.108 → 0.9.110
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/README.md +40 -13
- package/index.ts +4 -4
- package/lib/index.d.ts +4 -4
- package/lib/index.js +3 -3
- package/lib/src/agents.d.ts +6 -4
- package/lib/src/agents.js +127 -30
- package/lib/src/chat.js +4 -3
- package/lib/src/config.d.ts +21 -4
- package/lib/src/config.js +11 -3
- package/lib/src/memory.d.ts +8 -0
- package/lib/src/memory.js +24 -1
- package/lib/src/plugin.js +5 -5
- package/lib/src/remote-skills.d.ts +22 -9
- package/lib/src/remote-skills.js +54 -13
- package/lib/src/sandbox.d.ts +2 -1
- package/lib/src/sandbox.js +2 -2
- package/lib/src/workspaces.d.ts +15 -12
- package/lib/src/workspaces.js +32 -19
- package/package.json +17 -15
- package/src/agents.ts +166 -42
- package/src/chat.ts +9 -4
- package/src/config.ts +34 -6
- package/src/memory.ts +33 -0
- package/src/plugin.ts +4 -4
- package/src/remote-skills.ts +74 -16
- package/src/sandbox.ts +3 -2
- package/src/workspaces.ts +48 -27
package/src/remote-skills.ts
CHANGED
|
@@ -5,8 +5,8 @@
|
|
|
5
5
|
* {@link AITOOLS_SOURCE} constant, a GitHub `owner/repo`, any git / GitLab URL,
|
|
6
6
|
* or a direct download URL - and optional per-source policy.
|
|
7
7
|
* {@link provisionRemoteSkills} materializes every source into a local
|
|
8
|
-
* `SKILL.md` tree at app boot and returns
|
|
9
|
-
*
|
|
8
|
+
* `SKILL.md` tree at app boot and returns read-only backing directories plus
|
|
9
|
+
* their stable virtual workspace mounts.
|
|
10
10
|
*
|
|
11
11
|
* Resolution per source, in order:
|
|
12
12
|
*
|
|
@@ -43,8 +43,8 @@
|
|
|
43
43
|
* identity cannot WRITE the destination, which is the normal case for a
|
|
44
44
|
* Databricks App service principal against the admin-owned shared tree (see
|
|
45
45
|
* {@link openWritableWorkspace}) - the tree is written under
|
|
46
|
-
* {@link localFS.tmpFS} and
|
|
47
|
-
* current process. Storage location degrades; the skills themselves do not.
|
|
46
|
+
* {@link localFS.tmpFS} and exposed through a stable read-only virtual mount
|
|
47
|
+
* for the current process. Storage location degrades; the skills themselves do not.
|
|
48
48
|
*
|
|
49
49
|
* @module
|
|
50
50
|
*/
|
|
@@ -100,6 +100,12 @@ const DEFAULT_DOWNLOAD_TIMEOUT_MS = 60_000;
|
|
|
100
100
|
/** Stable temp directory holding one rebuilt skill tree per remote source. */
|
|
101
101
|
const LOCAL_SKILLS_DIR = "mastra-local-skills";
|
|
102
102
|
|
|
103
|
+
/** Virtual workspace path reserved for locally provisioned Databricks AI Tools. */
|
|
104
|
+
export const AITOOLS_SKILLS_MOUNT = "/.databricks/skills";
|
|
105
|
+
|
|
106
|
+
/** Virtual workspace root for every other locally provisioned remote source. */
|
|
107
|
+
export const REMOTE_SKILLS_MOUNT = "/.skills";
|
|
108
|
+
|
|
103
109
|
/**
|
|
104
110
|
* Bookkeeping file written at the root of every provisioned skill tree, naming
|
|
105
111
|
* when each source was last downloaded. A dotfile so the skill scanners, which
|
|
@@ -156,6 +162,8 @@ interface AiToolsManifest {
|
|
|
156
162
|
export interface RemoteSkillSourceOptions {
|
|
157
163
|
/** `"aitools"`, a GitHub shorthand, a git / GitLab URL, or a download URL. */
|
|
158
164
|
source: net.UrlLike | AiToolsSource;
|
|
165
|
+
/** Stable virtual mount name under `/.skills`; generated from the source when omitted. */
|
|
166
|
+
name?: string;
|
|
159
167
|
/** Install only these skill names from the source. */
|
|
160
168
|
skills?: string | string[];
|
|
161
169
|
/** Include experimental skills. `"aitools"` only. */
|
|
@@ -195,6 +203,14 @@ export type RemoteSkillsOption =
|
|
|
195
203
|
/** A source entry with its `source` flattened to a plain string. */
|
|
196
204
|
type NormalizedSource = Omit<RemoteSkillSourceOptions, "source"> & { source: string };
|
|
197
205
|
|
|
206
|
+
/** Read-only local backing storage exposed at a stable virtual workspace path. */
|
|
207
|
+
export interface LocalSkillMount {
|
|
208
|
+
/** Physical local directory containing the provisioned `SKILL.md` tree. */
|
|
209
|
+
root: string;
|
|
210
|
+
/** Virtual path agents see inside the composed Mastra workspace. */
|
|
211
|
+
mountPath: string;
|
|
212
|
+
}
|
|
213
|
+
|
|
198
214
|
/** Top-level remote-skills provisioning options. */
|
|
199
215
|
export interface ProvisionRemoteSkillsOptions {
|
|
200
216
|
/** Sources to materialize at startup. */
|
|
@@ -226,11 +242,11 @@ export interface ProvisionRemoteSkillsOptions {
|
|
|
226
242
|
/** What {@link provisionRemoteSkills} resolved. */
|
|
227
243
|
export interface ProvisionedRemoteSkills {
|
|
228
244
|
/**
|
|
229
|
-
* Extra LOCAL skill
|
|
230
|
-
*
|
|
231
|
-
*
|
|
245
|
+
* Extra read-only LOCAL skill mounts to hand Mastra when a source couldn't
|
|
246
|
+
* be written to Databricks. Physical temp/cache paths remain hidden behind
|
|
247
|
+
* stable virtual workspace paths.
|
|
232
248
|
*/
|
|
233
|
-
|
|
249
|
+
localSkillMounts: LocalSkillMount[];
|
|
234
250
|
/** Absolute Databricks destination each source was written to, if any. */
|
|
235
251
|
databricksBasePath?: string;
|
|
236
252
|
/**
|
|
@@ -342,6 +358,29 @@ function cacheKey(sourceOptions: NormalizedSource): string {
|
|
|
342
358
|
);
|
|
343
359
|
}
|
|
344
360
|
|
|
361
|
+
/** Stable virtual workspace path for one locally provisioned source. */
|
|
362
|
+
function localSkillMountPath(sourceOptions: NormalizedSource, key: string): string {
|
|
363
|
+
if (isAiToolsSource(sourceOptions.source)) return AITOOLS_SKILLS_MOUNT;
|
|
364
|
+
const configuredName = stringUtils.trimToNull(sourceOptions.name);
|
|
365
|
+
const identifier = configuredName
|
|
366
|
+
? stringUtils.toSlug(configuredName)
|
|
367
|
+
: stringUtils.toSlug(remoteSourceIdentifier(sourceOptions.source)) || "source";
|
|
368
|
+
if (!identifier) {
|
|
369
|
+
throw new Error(
|
|
370
|
+
`remote skill source name must contain letters or numbers: "${configuredName}"`,
|
|
371
|
+
);
|
|
372
|
+
}
|
|
373
|
+
return posix.join(REMOTE_SKILLS_MOUNT, configuredName ? identifier : `${identifier}-${key}`);
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
/** Human-readable source portion used by generated virtual mount identifiers. */
|
|
377
|
+
function remoteSourceIdentifier(source: string): string {
|
|
378
|
+
const url = net.urlBuilder(source);
|
|
379
|
+
if (!url) return source;
|
|
380
|
+
const path = url.pathname.replace(/\.(git|md|zip|tar|tgz|gz)$/i, "");
|
|
381
|
+
return `${url.hostname}-${path}`;
|
|
382
|
+
}
|
|
383
|
+
|
|
345
384
|
/** The effective reuse window: per-source, then top-level, then seven days. */
|
|
346
385
|
function resolveRefreshTtl(
|
|
347
386
|
sourceOptions: NormalizedSource,
|
|
@@ -416,13 +455,13 @@ function localSkillsFS(key: string): LocalFileSystem {
|
|
|
416
455
|
*
|
|
417
456
|
* Writes each resolved `SKILL.md` tree to the Databricks user Assistant skills
|
|
418
457
|
* folder when a writable workspace is available, else under
|
|
419
|
-
* {@link localFS.tmpFS} returned in {@link ProvisionedRemoteSkills.
|
|
458
|
+
* {@link localFS.tmpFS} returned in {@link ProvisionedRemoteSkills.localSkillMounts}.
|
|
420
459
|
*/
|
|
421
460
|
export async function provisionRemoteSkills(
|
|
422
461
|
option: RemoteSkillsOption | undefined,
|
|
423
462
|
): Promise<ProvisionedRemoteSkills> {
|
|
424
463
|
const options = normalizeRemoteSkillsOption(option);
|
|
425
|
-
const empty: ProvisionedRemoteSkills = {
|
|
464
|
+
const empty: ProvisionedRemoteSkills = { localSkillMounts: [], skillNames: [] };
|
|
426
465
|
if (!options) return empty;
|
|
427
466
|
|
|
428
467
|
const failDefault = options.failOnError !== false;
|
|
@@ -437,7 +476,7 @@ export async function provisionRemoteSkills(
|
|
|
437
476
|
: undefined;
|
|
438
477
|
const databricksBasePath = destination ? requestedBasePath : undefined;
|
|
439
478
|
|
|
440
|
-
const
|
|
479
|
+
const localSkillMounts: LocalSkillMount[] = [];
|
|
441
480
|
const skillNames: string[] = [];
|
|
442
481
|
let staging: LocalFileSystem | undefined;
|
|
443
482
|
|
|
@@ -450,15 +489,16 @@ export async function provisionRemoteSkills(
|
|
|
450
489
|
// Where this source's bookkeeping lives: the shared Databricks tree, or
|
|
451
490
|
// the source's own stable local dir.
|
|
452
491
|
const key = cacheKey(sourceOptions);
|
|
492
|
+
const mountPath = localSkillMountPath(sourceOptions, key);
|
|
453
493
|
const cacheFS = destination ?? localSkillsFS(key);
|
|
454
494
|
const metadata = await readMetadata(cacheFS);
|
|
455
495
|
const cached = metadata?.sources[key];
|
|
456
496
|
if (cached && isFresh(cached, resolveRefreshTtl(sourceOptions, options))) {
|
|
457
497
|
skillNames.push(...cached.skills);
|
|
458
|
-
if (!destination)
|
|
498
|
+
if (!destination) localSkillMounts.push({ root: cacheFS.root, mountPath });
|
|
459
499
|
logger.info("remote skill ready", {
|
|
460
500
|
source: sourceOptions.source,
|
|
461
|
-
destination: databricksBasePath ??
|
|
501
|
+
destination: databricksBasePath ?? mountPath,
|
|
462
502
|
downloadedAt: cached.downloadedAt,
|
|
463
503
|
skills: cached.skills,
|
|
464
504
|
cached: true,
|
|
@@ -489,12 +529,15 @@ export async function provisionRemoteSkills(
|
|
|
489
529
|
// mark a failed provision as fresh and suppress the retry for a day.
|
|
490
530
|
await writeMetadata(destination, key, record);
|
|
491
531
|
} else {
|
|
492
|
-
|
|
532
|
+
localSkillMounts.push({
|
|
533
|
+
root: await persistLocally(key, staged, record),
|
|
534
|
+
mountPath,
|
|
535
|
+
});
|
|
493
536
|
}
|
|
494
537
|
skillNames.push(...record.skills);
|
|
495
538
|
logger.info("remote skill installed", {
|
|
496
539
|
source: sourceOptions.source,
|
|
497
|
-
destination: databricksBasePath ??
|
|
540
|
+
destination: databricksBasePath ?? localSkillMounts.at(-1)?.mountPath,
|
|
498
541
|
skills: record.skills,
|
|
499
542
|
});
|
|
500
543
|
} catch (err) {
|
|
@@ -516,7 +559,22 @@ export async function provisionRemoteSkills(
|
|
|
516
559
|
}
|
|
517
560
|
}
|
|
518
561
|
|
|
519
|
-
|
|
562
|
+
assertUniqueLocalSkillMounts(localSkillMounts);
|
|
563
|
+
return { localSkillMounts, databricksBasePath, skillNames };
|
|
564
|
+
}
|
|
565
|
+
|
|
566
|
+
/** Fail before workspace composition when two sources claim one virtual mount. */
|
|
567
|
+
function assertUniqueLocalSkillMounts(mounts: readonly LocalSkillMount[]): void {
|
|
568
|
+
const rootsByMount = new Map<string, string>();
|
|
569
|
+
for (const mount of mounts) {
|
|
570
|
+
const previousRoot = rootsByMount.get(mount.mountPath);
|
|
571
|
+
if (previousRoot && previousRoot !== mount.root) {
|
|
572
|
+
throw new Error(
|
|
573
|
+
`multiple remote skill sources resolve to virtual mount "${mount.mountPath}"; give each source a unique name`,
|
|
574
|
+
);
|
|
575
|
+
}
|
|
576
|
+
rootsByMount.set(mount.mountPath, mount.root);
|
|
577
|
+
}
|
|
520
578
|
}
|
|
521
579
|
|
|
522
580
|
/** Remove directories dropped by one source unless another source still owns them. */
|
package/src/sandbox.ts
CHANGED
|
@@ -68,7 +68,8 @@ export interface DatabricksSandboxOptions {
|
|
|
68
68
|
commandTimeoutMs?: number;
|
|
69
69
|
/**
|
|
70
70
|
* Provider used only when Databricks Sandbox is definitively unavailable.
|
|
71
|
-
*
|
|
71
|
+
* Disabled by default. Pass `"monty"`, Monty options, or another Mastra
|
|
72
|
+
* sandbox to enable a fallback explicitly.
|
|
72
73
|
*/
|
|
73
74
|
fallback?: false | "monty" | MontySandboxOptions | WorkspaceSandbox;
|
|
74
75
|
}
|
|
@@ -123,7 +124,7 @@ export class DatabricksSandbox implements WorkspaceSandbox {
|
|
|
123
124
|
this.inactivityTimeout = options.inactivityTimeout ?? DEFAULT_INACTIVITY_TIMEOUT;
|
|
124
125
|
this.startupTimeoutMs = options.startupTimeoutMs ?? DEFAULT_STARTUP_TIMEOUT_MS;
|
|
125
126
|
this.commandTimeoutMs = options.commandTimeoutMs ?? DEFAULT_COMMAND_TIMEOUT_MS;
|
|
126
|
-
this.fallbackConfig = options.fallback ??
|
|
127
|
+
this.fallbackConfig = options.fallback ?? false;
|
|
127
128
|
}
|
|
128
129
|
|
|
129
130
|
get provider(): string {
|
package/src/workspaces.ts
CHANGED
|
@@ -60,6 +60,7 @@ import {
|
|
|
60
60
|
type MastraFileSystemAdapterOptions,
|
|
61
61
|
} from "./filesystems.ts";
|
|
62
62
|
import { MontySandbox } from "./monty-sandbox.ts";
|
|
63
|
+
import type { LocalSkillMount } from "./remote-skills.ts";
|
|
63
64
|
import { DatabricksSandbox, type DatabricksWorkspaceSandboxOptions } from "./sandbox.ts";
|
|
64
65
|
import { ORGANIZATION_ASSISTANT_PATH, personalWorkspacePath } from "./skill-paths.ts";
|
|
65
66
|
|
|
@@ -172,9 +173,9 @@ type WorkspaceMountResolver = (
|
|
|
172
173
|
) => WorkspaceMountContribution | Promise<WorkspaceMountContribution>;
|
|
173
174
|
|
|
174
175
|
/**
|
|
175
|
-
* Sandbox selection for {@link databricksWorkspace}.
|
|
176
|
-
*
|
|
177
|
-
*
|
|
176
|
+
* Sandbox selection for {@link databricksWorkspace}. Command execution is
|
|
177
|
+
* disabled by default; Monty, Databricks Sandbox, or a Mastra provider/resolver
|
|
178
|
+
* must be selected explicitly.
|
|
178
179
|
*/
|
|
179
180
|
export type WorkspaceSandboxSelection =
|
|
180
181
|
| false
|
|
@@ -207,19 +208,21 @@ export interface DatabricksWorkspaceOptions extends Omit<
|
|
|
207
208
|
/** AppKit plugin context used to discover optional sibling capabilities. */
|
|
208
209
|
pluginContext?: pluginRegistry.PluginContextLike;
|
|
209
210
|
/**
|
|
210
|
-
* Command sandbox.
|
|
211
|
-
*
|
|
212
|
-
* explicit Mastra sandbox/provider resolver.
|
|
211
|
+
* Command sandbox. Disabled by default. Pass `"monty"`, `"databricks"`, or
|
|
212
|
+
* Databricks Sandbox options to enable one of the built-in providers, or pass
|
|
213
|
+
* an explicit Mastra sandbox/provider resolver.
|
|
213
214
|
*/
|
|
214
215
|
sandbox?: WorkspaceSandboxSelection;
|
|
216
|
+
/** Add user-scoped `/tmp` only when no configured Databricks path mounts writable. */
|
|
217
|
+
fallbackToTmp?: boolean;
|
|
215
218
|
/**
|
|
216
|
-
* Extra LOCAL skill
|
|
217
|
-
* Used by the plugin to
|
|
218
|
-
*
|
|
219
|
-
* need no entry here
|
|
220
|
-
* already scans.
|
|
219
|
+
* Extra LOCAL skill trees mounted read-only for every request's skill
|
|
220
|
+
* discovery. Used by the plugin to expose remote skills from local backing
|
|
221
|
+
* storage at stable virtual paths (see `remote-skills.ts`). Databricks-hosted
|
|
222
|
+
* remote skills need no entry here because the built-in Assistant mount
|
|
223
|
+
* already scans them.
|
|
221
224
|
*/
|
|
222
|
-
|
|
225
|
+
extraSkillMounts?: LocalSkillMount[];
|
|
223
226
|
}
|
|
224
227
|
|
|
225
228
|
/* ------------------------------- defaults ------------------------------- */
|
|
@@ -273,7 +276,7 @@ export function databricksWorkspaceConfig(
|
|
|
273
276
|
const filesystemSourceKey = `${id}:${++workspaceSourceSequence}`;
|
|
274
277
|
const paths = resolveDatabricksWorkspacePaths(options);
|
|
275
278
|
const configuredPaths = paths.length;
|
|
276
|
-
const
|
|
279
|
+
const extraSkillMounts = options.extraSkillMounts ?? [];
|
|
277
280
|
const filesCache = pluginRegistry.instance(options.pluginContext, filesCachePlugin)?.exports();
|
|
278
281
|
const configuredCache = options.cache;
|
|
279
282
|
const mountFilesCache = configuredCache === false ? undefined : filesCache;
|
|
@@ -284,9 +287,10 @@ export function databricksWorkspaceConfig(
|
|
|
284
287
|
const resolvers = buildMountResolvers(
|
|
285
288
|
paths,
|
|
286
289
|
options.mounts,
|
|
287
|
-
localSkillMountResolvers(
|
|
290
|
+
localSkillMountResolvers(extraSkillMounts),
|
|
288
291
|
mountFilesCache,
|
|
289
292
|
configuredCache,
|
|
293
|
+
options.fallbackToTmp === true,
|
|
290
294
|
);
|
|
291
295
|
const resolveContribution = contributionResolver(resolvers);
|
|
292
296
|
const resolveFilesystem = (context: DatabricksWorkspaceContext) =>
|
|
@@ -296,7 +300,7 @@ export function databricksWorkspaceConfig(
|
|
|
296
300
|
retainFilesystemSource ? mountFilesCache : undefined,
|
|
297
301
|
filesystemSourceKey,
|
|
298
302
|
);
|
|
299
|
-
const hasSkillPaths = paths.some(pathMayProvideSkills) ||
|
|
303
|
+
const hasSkillPaths = paths.some(pathMayProvideSkills) || extraSkillMounts.length > 0;
|
|
300
304
|
const skills: SkillsResolver =
|
|
301
305
|
options.skills ??
|
|
302
306
|
(async ({ requestContext }) => {
|
|
@@ -321,7 +325,7 @@ export function databricksWorkspaceConfig(
|
|
|
321
325
|
customSkillsResolver: options.skills !== undefined,
|
|
322
326
|
checkSkillFileMtime,
|
|
323
327
|
bm25,
|
|
324
|
-
|
|
328
|
+
extraSkillMounts: extraSkillMounts.length,
|
|
325
329
|
sandbox: sandbox ? sandboxName(options.sandbox) : "disabled",
|
|
326
330
|
});
|
|
327
331
|
|
|
@@ -333,7 +337,8 @@ export function databricksWorkspaceConfig(
|
|
|
333
337
|
cache: _cache,
|
|
334
338
|
pluginContext: _pluginContext,
|
|
335
339
|
sandbox: _sandbox,
|
|
336
|
-
|
|
340
|
+
fallbackToTmp: _fallbackToTmp,
|
|
341
|
+
extraSkillMounts: _extraSkillMounts,
|
|
337
342
|
...workspaceOptions
|
|
338
343
|
} = options;
|
|
339
344
|
return {
|
|
@@ -411,7 +416,7 @@ function resolveWorkspaceSandbox(
|
|
|
411
416
|
workspaceId: string,
|
|
412
417
|
workspaceName: string,
|
|
413
418
|
): WorkspaceSandbox | WorkspaceSandboxResolver | undefined {
|
|
414
|
-
const configured = selection ??
|
|
419
|
+
const configured = selection ?? false;
|
|
415
420
|
if (configured === false) return undefined;
|
|
416
421
|
if (configured === "monty") return new MontySandbox();
|
|
417
422
|
if (typeof configured === "function" || isSandbox(configured)) return configured;
|
|
@@ -465,9 +470,9 @@ function isSandbox(value: unknown): value is WorkspaceSandbox {
|
|
|
465
470
|
}
|
|
466
471
|
|
|
467
472
|
function sandboxName(selection: WorkspaceSandboxSelection | undefined): string {
|
|
468
|
-
if (selection === undefined || selection ===
|
|
473
|
+
if (selection === undefined || selection === false) return "disabled";
|
|
474
|
+
if (selection === "monty") return "monty";
|
|
469
475
|
if (selection === "databricks") return "databricks";
|
|
470
|
-
if (selection === false) return "disabled";
|
|
471
476
|
if (typeof selection === "function") return "resolver";
|
|
472
477
|
return isSandbox(selection) ? selection.provider : (selection.provider ?? "databricks");
|
|
473
478
|
}
|
|
@@ -536,9 +541,11 @@ async function resolveWorkspacePathMounts(
|
|
|
536
541
|
context: DatabricksWorkspaceContext,
|
|
537
542
|
filesCache: FilesCacheExports | undefined,
|
|
538
543
|
cacheConfig: DatabricksWorkspaceCache | undefined,
|
|
544
|
+
fallbackToTmp: boolean,
|
|
539
545
|
): Promise<WorkspaceMountContribution> {
|
|
540
546
|
const mounts: Record<string, WorkspaceFilesystem> = {};
|
|
541
547
|
const skillPaths: string[] = [];
|
|
548
|
+
let hasWritableMount = false;
|
|
542
549
|
const requestContext = context.requestContext;
|
|
543
550
|
|
|
544
551
|
const canMountDatabricks = Boolean(requestContext && shouldMountSkillFolders(requestContext));
|
|
@@ -553,6 +560,7 @@ async function resolveWorkspacePathMounts(
|
|
|
553
560
|
if (posixPath.isWithinRoot(SCRATCH_MOUNT, configured.path)) {
|
|
554
561
|
const mount = configured.mount ?? configured.path;
|
|
555
562
|
mounts[mount] = userTempFilesystem(context, configured.path);
|
|
563
|
+
hasWritableMount = true;
|
|
556
564
|
skillPaths.push(...(configured.skills ?? []).map((path) => mountedSkillPath(mount, path)));
|
|
557
565
|
continue;
|
|
558
566
|
}
|
|
@@ -574,9 +582,14 @@ async function resolveWorkspacePathMounts(
|
|
|
574
582
|
continue;
|
|
575
583
|
}
|
|
576
584
|
mounts[mount] = resolved.filesystem;
|
|
585
|
+
hasWritableMount ||= resolved.writable;
|
|
577
586
|
skillPaths.push(...(configured.skills ?? []).map((path) => mountedSkillPath(mount, path)));
|
|
578
587
|
}
|
|
579
588
|
|
|
589
|
+
if (fallbackToTmp && !hasWritableMount) {
|
|
590
|
+
mounts[SCRATCH_MOUNT] = userTempFilesystem(context, SCRATCH_MOUNT);
|
|
591
|
+
}
|
|
592
|
+
|
|
580
593
|
logger.debug("workspace-paths:mounted", {
|
|
581
594
|
mountKeys: Object.keys(mounts),
|
|
582
595
|
skillPaths,
|
|
@@ -632,6 +645,7 @@ async function resolveSkillFolderFilesystem(
|
|
|
632
645
|
| {
|
|
633
646
|
filesystem: WorkspaceFilesystem;
|
|
634
647
|
root?: string;
|
|
648
|
+
writable: boolean;
|
|
635
649
|
}
|
|
636
650
|
| undefined
|
|
637
651
|
> {
|
|
@@ -661,7 +675,7 @@ async function resolveSkillFolderFilesystem(
|
|
|
661
675
|
...(folder.displayName ? { displayName: folder.displayName } : {}),
|
|
662
676
|
},
|
|
663
677
|
);
|
|
664
|
-
return resolved ? { ...resolved, root } : undefined;
|
|
678
|
+
return resolved ? { ...resolved, root, writable } : undefined;
|
|
665
679
|
}
|
|
666
680
|
|
|
667
681
|
async function resolveCacheConfig(
|
|
@@ -782,12 +796,13 @@ function buildMountResolvers(
|
|
|
782
796
|
localMounts: WorkspaceMountResolver[],
|
|
783
797
|
filesCache: FilesCacheExports | undefined,
|
|
784
798
|
cacheConfig: DatabricksWorkspaceCache | undefined,
|
|
799
|
+
fallbackToTmp: boolean,
|
|
785
800
|
): WorkspaceMountResolver[] {
|
|
786
801
|
const resolvers: WorkspaceMountResolver[] = [];
|
|
787
802
|
const pathCount = paths.length;
|
|
788
|
-
if (pathCount > 0) {
|
|
803
|
+
if (pathCount > 0 || fallbackToTmp) {
|
|
789
804
|
resolvers.push((context) =>
|
|
790
|
-
resolveWorkspacePathMounts(paths, context, filesCache, cacheConfig),
|
|
805
|
+
resolveWorkspacePathMounts(paths, context, filesCache, cacheConfig, fallbackToTmp),
|
|
791
806
|
);
|
|
792
807
|
}
|
|
793
808
|
if (mounts && Object.keys(mounts).length > 0) resolvers.push(() => ({ mounts }));
|
|
@@ -825,10 +840,16 @@ function userTempFilesystem(
|
|
|
825
840
|
});
|
|
826
841
|
}
|
|
827
842
|
|
|
828
|
-
/** Mount startup-provisioned local skill
|
|
829
|
-
function localSkillMountResolvers(
|
|
830
|
-
|
|
831
|
-
|
|
843
|
+
/** Mount startup-provisioned local skill trees into Mastra's workspace filesystem. */
|
|
844
|
+
function localSkillMountResolvers(mounts: readonly LocalSkillMount[]): WorkspaceMountResolver[] {
|
|
845
|
+
const unique = new Map<string, LocalSkillMount>();
|
|
846
|
+
for (const entry of mounts) {
|
|
847
|
+
const root = entry.root.trim();
|
|
848
|
+
if (!root) continue;
|
|
849
|
+
unique.set(`${entry.mountPath}\0${root}`, { root, mountPath: entry.mountPath });
|
|
850
|
+
}
|
|
851
|
+
return [...unique.values()].map(({ root, mountPath }) => {
|
|
852
|
+
const mount = posixPath.normalizeRoot(mountPath);
|
|
832
853
|
const source = new LocalFileSystem({
|
|
833
854
|
root,
|
|
834
855
|
readOnly: true,
|