@dbx-tools/appkit-mastra 0.9.95 → 0.9.105

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.
@@ -9,13 +9,15 @@
9
9
  * @module
10
10
  */
11
11
 
12
+ import { availableParallelism } from "node:os";
12
13
  import { configUtils } from "@dbx-tools/core";
13
14
  import { errorUtils, functionUtils, log } from "@dbx-tools/shared-core";
14
- import type {
15
- CommandResult,
16
- ExecuteCommandOptions,
17
- SandboxInfo,
18
- WorkspaceSandbox,
15
+ import {
16
+ MastraSandbox,
17
+ type CommandResult,
18
+ type ExecuteCommandOptions,
19
+ type ProviderStatus,
20
+ type SandboxInfo,
19
21
  } from "@mastra/core/workspace";
20
22
  import type { CheckoutOptions, Monty, MontyCrashedError, MontySession } from "@pydantic/monty";
21
23
 
@@ -38,6 +40,11 @@ interface MontyContext {
38
40
  pool: MontyPool;
39
41
  }
40
42
 
43
+ /** @internal Size the shared worker pool from the host's available processors. */
44
+ export function montyMaxProcesses(parallelism: number = availableParallelism()): number {
45
+ return Math.max(4, Math.floor(parallelism) * 2);
46
+ }
47
+
41
48
  const montyContext = functionUtils.memoize(async (): Promise<MontyContext> => {
42
49
  const errors: Error[] = [];
43
50
  const targets = configUtils.boolean(undefined, "MONTY_FORCE_WASM")
@@ -47,12 +54,13 @@ const montyContext = functionUtils.memoize(async (): Promise<MontyContext> => {
47
54
  try {
48
55
  const module = await target.load();
49
56
  logger.debug(`${target.name} module available`);
57
+ const maxProcesses = montyMaxProcesses();
50
58
  const pool = await module.Monty.create({
51
59
  minProcesses: 1,
52
- maxProcesses: 4,
60
+ maxProcesses,
53
61
  requestTimeout: 35,
54
62
  });
55
- logger.debug(`${target.name} pool created`);
63
+ logger.debug(`${target.name} pool created`, { maxProcesses });
56
64
  return {
57
65
  crashedError: module.MontyCrashedError,
58
66
  pool,
@@ -82,12 +90,11 @@ export interface MontySandboxOptions {
82
90
  }
83
91
 
84
92
  /** Python-only, deny-by-default sandbox backed by Monty's selected worker pool. */
85
- export class MontySandbox implements WorkspaceSandbox {
93
+ export class MontySandbox extends MastraSandbox {
86
94
  readonly id: string;
87
95
  readonly name: string;
88
96
  readonly provider = "monty";
89
- status: WorkspaceSandbox["status"] = "pending";
90
- error?: string;
97
+ status: ProviderStatus = "pending";
91
98
 
92
99
  private readonly commandTimeoutMs: number;
93
100
  private readonly maxMemoryBytes: number;
@@ -96,8 +103,10 @@ export class MontySandbox implements WorkspaceSandbox {
96
103
  private lastUsedAt?: Date;
97
104
 
98
105
  constructor(options: MontySandboxOptions = {}) {
106
+ const name = options.name ?? "Pydantic Monty";
107
+ super({ name });
99
108
  this.id = options.id ?? "monty";
100
- this.name = options.name ?? "Pydantic Monty";
109
+ this.name = name;
101
110
  this.commandTimeoutMs = options.commandTimeoutMs ?? DEFAULT_COMMAND_TIMEOUT_MS;
102
111
  this.maxMemoryBytes = options.maxMemoryBytes ?? DEFAULT_MAX_MEMORY_BYTES;
103
112
  this.typeCheck = options.typeCheck ?? false;
@@ -105,33 +114,14 @@ export class MontySandbox implements WorkspaceSandbox {
105
114
 
106
115
  /** Warm the shared crash-isolated worker pool. */
107
116
  async start(): Promise<void> {
108
- if (this.status === "running") return;
109
- this.status = "starting";
110
- this.error = undefined;
111
- try {
112
- await montyContext();
113
- this.status = "running";
114
- } catch (caught) {
115
- this.status = "error";
116
- this.error = errorUtils.errorMessage(caught);
117
- throw caught;
118
- }
117
+ await montyContext();
119
118
  }
120
119
 
121
120
  /** The process-wide pool stays warm; this instance carries no open session. */
122
- async stop(): Promise<void> {
123
- this.status = "stopped";
124
- }
121
+ async stop(): Promise<void> {}
125
122
 
126
123
  /** The process-wide pool stays warm; each command closes its own checkout. */
127
- async destroy(): Promise<void> {
128
- this.status = "destroyed";
129
- }
130
-
131
- /** Monty sessions are ephemeral and do not provide persistent checkpoints. */
132
- async snapshot(): Promise<void> {}
133
-
134
- readonly supportsCheckpoints = false;
124
+ async destroy(): Promise<void> {}
135
125
 
136
126
  /** Execute Python source and map Monty output to Mastra's command result. */
137
127
  async executeCommand(
@@ -9,16 +9,7 @@ import { urlBuilder, type UrlBuilder } from "@dbx-tools/shared-core/net";
9
9
  import type { JSONSchema7 } from "json-schema";
10
10
  import { z } from "zod";
11
11
 
12
- const HTTP_METHODS = [
13
- "GET",
14
- "POST",
15
- "PUT",
16
- "PATCH",
17
- "DELETE",
18
- "OPTIONS",
19
- "HEAD",
20
- "TRACE",
21
- ] as const;
12
+ const HTTP_METHODS = ["GET", "POST", "PUT", "PATCH", "DELETE", "OPTIONS", "HEAD", "TRACE"] as const;
22
13
 
23
14
  const JsonObjectSchema = z
24
15
  .record(z.string(), z.unknown())
@@ -208,7 +199,7 @@ function operationUrl(document: OpenApiDocument, sourceUrl: URL | undefined, pat
208
199
  ? base.withPathAppend(path).toString()
209
200
  : `${server.replace(/\/$/, "")}/${path.replace(/^\//, "")}`;
210
201
  }
211
- return sourceUrl ? urlBuilder(sourceUrl)?.withPathReplace(path).toString() ?? path : path;
202
+ return sourceUrl ? (urlBuilder(sourceUrl)?.withPathReplace(path).toString() ?? path) : path;
212
203
  }
213
204
 
214
205
  function resolveReferences(
package/src/plugin.ts CHANGED
@@ -458,6 +458,7 @@ export class MastraPlugin extends Plugin<MastraPluginConfig> {
458
458
  defaultAgent: this.built?.defaultAgentId ?? FALLBACK_AGENT_ID,
459
459
  agents: Object.keys(this.built?.agents ?? {}),
460
460
  feedbackEnabled: this.feedbackEnabled(),
461
+ backgroundTurns: this.config.backgroundTurns !== false,
461
462
  // In `service-principal` mode the chat works for any caller who can open
462
463
  // the app, so a UI that hides chat behind an OBO-token probe should show
463
464
  // it regardless. `"user"` mode keeps the OBO-gated behavior.
@@ -1190,6 +1191,7 @@ export class MastraPlugin extends Plugin<MastraPluginConfig> {
1190
1191
  this.logger.info("ready", {
1191
1192
  agents: Object.keys(this.built.agents),
1192
1193
  defaultAgent: this.built.defaultAgentId,
1194
+ backgroundTurns: this.config.backgroundTurns !== false,
1193
1195
  apiAccess: this.config.apiAccess ?? "scoped",
1194
1196
  mcp: this.mcp ? `${this.basePath}${this.mcp.httpPath}` : "off",
1195
1197
  lakebase: memoryBuilder !== undefined,
package/src/server.ts CHANGED
@@ -470,25 +470,27 @@ export function attachRoutePatchMiddleware(app: express.Express): void {
470
470
  * completed turn.
471
471
  */
472
472
  export function attachStaleResumeRecovery(app: express.Express): void {
473
- app.use((error: unknown, req: express.Request, res: express.Response, next: express.NextFunction) => {
474
- if (!isStaleMastraResumeError(error)) {
475
- next(error);
476
- return;
477
- }
478
- logger.warn("ignored stale mastra resume", {
479
- path: req.path,
480
- error: errorUtils.errorMessage(error),
481
- });
482
- if (res.headersSent) {
473
+ app.use(
474
+ (error: unknown, req: express.Request, res: express.Response, next: express.NextFunction) => {
475
+ if (!isStaleMastraResumeError(error)) {
476
+ next(error);
477
+ return;
478
+ }
479
+ logger.warn("ignored stale mastra resume", {
480
+ path: req.path,
481
+ error: errorUtils.errorMessage(error),
482
+ });
483
+ if (res.headersSent) {
484
+ res.end();
485
+ return;
486
+ }
487
+ res.status(200);
488
+ res.setHeader("content-type", "text/event-stream; charset=utf-8");
489
+ res.setHeader("cache-control", "no-cache");
490
+ res.write(`data: ${JSON.stringify({ type: "start" })}\n\n`);
491
+ res.write(`data: ${JSON.stringify({ type: "finish" })}\n\n`);
492
+ res.write("data: [DONE]\n\n");
483
493
  res.end();
484
- return;
485
- }
486
- res.status(200);
487
- res.setHeader("content-type", "text/event-stream; charset=utf-8");
488
- res.setHeader("cache-control", "no-cache");
489
- res.write(`data: ${JSON.stringify({ type: "start" })}\n\n`);
490
- res.write(`data: ${JSON.stringify({ type: "finish" })}\n\n`);
491
- res.write("data: [DONE]\n\n");
492
- res.end();
493
- });
494
+ },
495
+ );
494
496
  }
@@ -2,7 +2,7 @@
2
2
  * Where Databricks Assistant `SKILL.md` trees live.
3
3
  *
4
4
  * Two modules need these paths and must agree on them: `workspaces.ts` MOUNTS
5
- * them per request (see `DEFAULT_SKILL_FOLDERS`), and `remote-skills.ts`
5
+ * them per request (see `DEFAULT_DATABRICKS_WORKSPACE_PATHS`), and `remote-skills.ts`
6
6
  * WRITES provisioned skills into them at startup. They live here rather than being
7
7
  * spelled out in both, where a change to one would silently provision skills into a
8
8
  * tree the other never scanned.