@axiom-lattice/core 2.1.72 → 2.1.74

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.mjs CHANGED
@@ -3059,6 +3059,168 @@ var PostgresDatabase = class {
3059
3059
  }
3060
3060
  }
3061
3061
  };
3062
+ var MysqlDatabase = class {
3063
+ constructor(config) {
3064
+ // mysql2.Pool
3065
+ this.connected = false;
3066
+ this.config = config;
3067
+ }
3068
+ async connect() {
3069
+ if (this.connected && this.pool) return;
3070
+ try {
3071
+ const mysql = await import("mysql2/promise");
3072
+ const poolConfig = this.config.connectionString ? { uri: this.config.connectionString } : {
3073
+ host: this.config.host || "localhost",
3074
+ port: this.config.port || 3306,
3075
+ database: this.config.database,
3076
+ user: this.config.user,
3077
+ password: this.config.password,
3078
+ ssl: this.config.ssl ? { rejectUnauthorized: false } : void 0
3079
+ };
3080
+ this.pool = mysql.createPool({
3081
+ ...poolConfig,
3082
+ waitForConnections: true,
3083
+ connectionLimit: 10,
3084
+ maxIdle: 10,
3085
+ idleTimeout: 6e4,
3086
+ queueLimit: 0,
3087
+ enableKeepAlive: true,
3088
+ keepAliveInitialDelay: 0
3089
+ });
3090
+ const connection = await this.pool.getConnection();
3091
+ try {
3092
+ await connection.query("SELECT 1");
3093
+ } finally {
3094
+ connection.release();
3095
+ }
3096
+ this.connected = true;
3097
+ } catch (error) {
3098
+ this.connected = false;
3099
+ throw new Error(`Failed to connect to MySQL: ${error}`);
3100
+ }
3101
+ }
3102
+ async disconnect() {
3103
+ if (this.pool) {
3104
+ try {
3105
+ await this.pool.end();
3106
+ } catch (error) {
3107
+ console.warn("Warning: Error closing MySQL pool:", error);
3108
+ } finally {
3109
+ this.pool = null;
3110
+ this.connected = false;
3111
+ }
3112
+ }
3113
+ }
3114
+ async listTables() {
3115
+ await this.ensureConnected();
3116
+ const query = `
3117
+ SELECT TABLE_NAME, TABLE_SCHEMA
3118
+ FROM information_schema.TABLES
3119
+ WHERE TABLE_SCHEMA NOT IN ('mysql', 'information_schema', 'performance_schema', 'sys')
3120
+ AND TABLE_SCHEMA = DATABASE()
3121
+ AND TABLE_TYPE = 'BASE TABLE'
3122
+ ORDER BY TABLE_SCHEMA, TABLE_NAME
3123
+ `;
3124
+ const [rows] = await this.pool.query(query);
3125
+ return rows.map((row) => ({
3126
+ name: row.TABLE_NAME,
3127
+ schema: row.TABLE_SCHEMA
3128
+ }));
3129
+ }
3130
+ async getTableInfo(tables) {
3131
+ await this.ensureConnected();
3132
+ const schemas = [];
3133
+ for (const tableName of tables) {
3134
+ const rawName = tableName.includes(".") ? tableName.split(".").pop() : tableName;
3135
+ const columnQuery = `
3136
+ SELECT
3137
+ c.COLUMN_NAME,
3138
+ c.DATA_TYPE,
3139
+ c.IS_NULLABLE,
3140
+ c.COLUMN_DEFAULT
3141
+ FROM information_schema.COLUMNS c
3142
+ WHERE c.TABLE_NAME = ?
3143
+ AND c.TABLE_SCHEMA = DATABASE()
3144
+ ORDER BY c.ORDINAL_POSITION
3145
+ `;
3146
+ const pkQuery = `
3147
+ SELECT k.COLUMN_NAME
3148
+ FROM information_schema.TABLE_CONSTRAINTS t
3149
+ JOIN information_schema.KEY_COLUMN_USAGE k
3150
+ ON t.CONSTRAINT_NAME = k.CONSTRAINT_NAME
3151
+ AND t.TABLE_SCHEMA = k.TABLE_SCHEMA
3152
+ AND t.TABLE_NAME = k.TABLE_NAME
3153
+ WHERE t.CONSTRAINT_TYPE = 'PRIMARY KEY'
3154
+ AND t.TABLE_NAME = ?
3155
+ AND t.TABLE_SCHEMA = DATABASE()
3156
+ `;
3157
+ const fkQuery = `
3158
+ SELECT
3159
+ k.COLUMN_NAME,
3160
+ k.REFERENCED_TABLE_NAME,
3161
+ k.REFERENCED_COLUMN_NAME
3162
+ FROM information_schema.KEY_COLUMN_USAGE k
3163
+ WHERE k.TABLE_NAME = ?
3164
+ AND k.TABLE_SCHEMA = DATABASE()
3165
+ AND k.REFERENCED_TABLE_NAME IS NOT NULL
3166
+ `;
3167
+ const [columnRows] = await this.pool.query(columnQuery, [rawName]);
3168
+ const [pkRows] = await this.pool.query(pkQuery, [rawName]);
3169
+ const [fkRows] = await this.pool.query(fkQuery, [rawName]);
3170
+ const pkColumns = new Set(pkRows.map((r) => r.COLUMN_NAME));
3171
+ const fkMap = /* @__PURE__ */ new Map();
3172
+ for (const row of fkRows) {
3173
+ fkMap.set(row.COLUMN_NAME, {
3174
+ foreignTable: row.REFERENCED_TABLE_NAME,
3175
+ foreignColumn: row.REFERENCED_COLUMN_NAME
3176
+ });
3177
+ }
3178
+ const columns = columnRows.map((row) => {
3179
+ const fkRef = fkMap.get(row.COLUMN_NAME);
3180
+ return {
3181
+ name: row.COLUMN_NAME,
3182
+ type: row.DATA_TYPE,
3183
+ nullable: row.IS_NULLABLE === "YES",
3184
+ default: row.COLUMN_DEFAULT,
3185
+ isPrimaryKey: pkColumns.has(row.COLUMN_NAME),
3186
+ isForeignKey: fkRef !== void 0,
3187
+ foreignKeyRef: fkRef ? `${fkRef.foreignTable}.${fkRef.foreignColumn}` : void 0
3188
+ };
3189
+ });
3190
+ let sampleRows = [];
3191
+ try {
3192
+ const sampleQuery = `SELECT * FROM \`${rawName}\` LIMIT 3`;
3193
+ const [sampleResult] = await this.pool.query(sampleQuery);
3194
+ sampleRows = sampleResult;
3195
+ } catch {
3196
+ }
3197
+ schemas.push({
3198
+ tableName,
3199
+ columns,
3200
+ sampleRows
3201
+ });
3202
+ }
3203
+ return schemas;
3204
+ }
3205
+ async executeQuery(query) {
3206
+ await this.ensureConnected();
3207
+ const [rows, fields] = await this.pool.query(query);
3208
+ const isSelectResult = Array.isArray(rows);
3209
+ return {
3210
+ rows: isSelectResult ? rows : [],
3211
+ rowCount: isSelectResult ? rows.length : rows.affectedRows || 0,
3212
+ fields: fields?.map((f) => f.name)
3213
+ };
3214
+ }
3215
+ getDatabaseType() {
3216
+ return "mysql";
3217
+ }
3218
+ async ensureConnected() {
3219
+ if (!this.connected) {
3220
+ await this.connect();
3221
+ }
3222
+ }
3223
+ };
3062
3224
  var SqlDatabaseManager = class _SqlDatabaseManager {
3063
3225
  constructor() {
3064
3226
  this.databases = /* @__PURE__ */ new Map();
@@ -3098,7 +3260,8 @@ var SqlDatabaseManager = class _SqlDatabaseManager {
3098
3260
  database = new PostgresDatabase(config);
3099
3261
  break;
3100
3262
  case "mysql":
3101
- throw new Error("MySQL support not yet implemented");
3263
+ database = new MysqlDatabase(config);
3264
+ break;
3102
3265
  case "sqlite":
3103
3266
  throw new Error("SQLite support not yet implemented");
3104
3267
  default:
@@ -5633,7 +5796,7 @@ var SandboxLatticeManager = class _SandboxLatticeManager extends BaseLatticeMana
5633
5796
  const tenantId = config.tenantId ?? "default";
5634
5797
  const mapping = this._resolveVolumeForPath(config, tenantId, filePath);
5635
5798
  if (!mapping) return null;
5636
- const client = provider.createVolumeFsClient(mapping.volumeName);
5799
+ const client = provider.createVolumeFsClient(mapping.volumeName, mapping.pathPrefix);
5637
5800
  return new VolumeFilesystem(stripPrefixClient(client, mapping.pathPrefix), mapping.pathPrefix);
5638
5801
  }
5639
5802
  _resolveVolumeForPath(config, tenantId, filePath) {
@@ -18896,10 +19059,105 @@ Use this when the task follows a standard BPO (Business Process Orchestration) p
18896
19059
 
18897
19060
  **Business rule \u2014 Draft-then-confirm pattern:** When a workflow creates business objects that require human approval (e.g., draft orders in SAP B1, purchase requisitions, expense reports), the design MUST include a confirmation step AFTER the creation step. The create step must complete successfully before the confirm step runs. The flow is always: **Create draft \u2192 User confirms \u2192 Finalize**. Reason: systems like SAP B1 only expose drafts to users after they are created \u2014 the user cannot see or approve something that doesn't exist yet. The confirm step MUST use the \`ask_user_to_clarify\` middleware.
18898
19061
 
19062
+ **Business pattern \u2014 SAP B1 Draft SO Creation:** When designing a sub-agent responsible for creating draft Sales Orders in SAP B1, embed the following standards into its system prompt. These ensure consistent data construction, API usage, and user presentation:
19063
+
19064
+ **Step 1: Construct SO Data Structure**
19065
+ Build the JSON payload. For each DocumentLines item:
19066
+ - Include \`WarehouseCode\` ONLY if sku.json provides a value; if missing or empty, OMIT the field entirely
19067
+ - Generate \`Comments\` dynamically based on context (customer name, PO reference, special instructions)
19068
+
19069
+ \`\`\`json
19070
+ {
19071
+ "DocObjectCode": "17",
19072
+ "DocType": "dDocument_Items",
19073
+ "CardCode": "C001",
19074
+ "DocDate": "current date",
19075
+ "DocDueDate": "delivery date",
19076
+ "TaxDate": "current date",
19077
+ "U_YWLX": "Modern Trade",
19078
+ "Comments": "SO for Kaimay Retail - PO ref: PO-2024-001234",
19079
+ "DocumentLines": [
19080
+ {
19081
+ "ItemCode": "SKU001",
19082
+ "Quantity": 240,
19083
+ "UnitPrice": 15.84
19084
+ }
19085
+ ]
19086
+ }
19087
+ \`\`\`
19088
+
19089
+ Field mapping rules:
19090
+ | SAP Field | Data Source |
19091
+ |-----------|-------------|
19092
+ | CardCode | customer.json \u2192 CardCode |
19093
+ | DocDate | Current date (yyyy-MM-dd) |
19094
+ | DocDueDate | extracted.json \u2192 delivery_date |
19095
+ | TaxDate | Current date (yyyy-MM-dd) |
19096
+ | U_YWLX | customer.json \u2192 SalesType |
19097
+ | ItemCode | sku.json \u2192 ItemCode |
19098
+ | Quantity | extracted.json \u2192 Quantity |
19099
+ | WarehouseCode | sku.json \u2192 WarehouseCode (OMIT if missing/empty) |
19100
+ | UnitPrice | price.json \u2192 UnitPrice |
19101
+ | Comments | AI-generated based on customer name, PO reference, and context |
19102
+
19103
+ **Step 2: Call SAP API to Create Draft (MUST EXECUTE)**
19104
+ Use the \`sap_api_call\` tool with EXACTLY these parameters:
19105
+ - **Method**: POST
19106
+ - **Endpoint**: \`Drafts\` (ONLY allowed endpoint; do NOT call SalesOrders or any other endpoint)
19107
+ - **Body**: The JSON constructed in Step 1
19108
+
19109
+ CRITICAL: You MUST call sap_api_call BEFORE proceeding to Step 3. This step is NOT optional.
19110
+
19111
+ If the API call fails:
19112
+ - Log the error details
19113
+ - STOP the process
19114
+ - Report the failure to the user with the error message
19115
+ - Do NOT proceed to Step 3
19116
+
19117
+ **Step 3: Present Draft for User Review (MUST EXECUTE AFTER Step 2)**
19118
+ ONLY after the SAP API call in Step 2 succeeds, use the \`ask_user_to_clarify\` tool to present the complete SO summary.
19119
+
19120
+ Message format (STRICT \u2014 no emoji, no summary, field names match Service Layer):
19121
+
19122
+ === Sales Order Draft ===
19123
+
19124
+ DocEntry: {DocEntry}
19125
+ DocNum: {DocNum}
19126
+ DocDate: {DocDate}
19127
+ DocDueDate: {DocDueDate}
19128
+ CardCode: {CardCode}
19129
+ CardName: {CardName}
19130
+ U_YWLX: {SalesType}
19131
+ Comments: {Comments}
19132
+
19133
+ DocumentLines:
19134
+ LineNum | ItemCode | Quantity | WarehouseCode | UnitPrice | LineTotal
19135
+ 0 | {ItemCode} | {Quantity} | {WarehouseCode} | {UnitPrice} | {LineTotal}
19136
+ 1 | {ItemCode} | {Quantity} | {WarehouseCode} | {UnitPrice} | {LineTotal}
19137
+ ...
19138
+
19139
+ DocTotal: {DocTotal}
19140
+
19141
+ Warnings:
19142
+ - {warning1}
19143
+ - {warning2}
19144
+
19145
+ Options:
19146
+ [Confirm generating draft]
19147
+ [Cancel, do not generate]
19148
+
19149
+ **Data sharing pattern \u2014 File-based handoff:** Every sub-agent in the pipeline MUST persist its work results as files under a shared project directory, never rely on in-memory data passing between steps. Deep agents have built-in file capabilities, so no middleware is needed.
19150
+
19151
+ Rules:
19152
+ 1. The **orchestrator** generates a unique project ID at the start (e.g., timestamp-based) and passes it to each sub-agent in the task context
19153
+ 2. Each sub-agent reads input from \`/project/{project-id}/\` and writes output files into the same directory
19154
+ 3. Establish clear file naming conventions so downstream steps know exactly which files to read (e.g., \`extracted.json\`, \`sku.json\`, \`customer.json\`, \`so_draft.json\`)
19155
+ 4. The orchestrator's system prompt MUST specify for each edge: which files the target sub-agent should read and which files it should produce
19156
+
18899
19157
  **Step 2: Topology design.** Design the agent topology and use \`show_widget\` to render an interactive diagram showing the flow: Orchestrator \u2192 Stage 1 \u2192 Stage 2 \u2192 ... \u2192 Output. Each edge should be labeled with its business purpose.
18900
19158
 
18901
19159
  **Step 3: Design each sub-agent** as a **deep_agent** type (one at a time). For each:
18902
- - Responsibility, system prompt, middleware, input/output contract
19160
+ - Responsibility, system prompt, middleware, file input/output contract (which files from \`/project/{project-id}/\` it reads, which files it writes)
18903
19161
  - Deep agents have built-in file capabilities (ls, read, write, edit, glob, grep), so do NOT add filesystem middleware.
18904
19162
  - **Ask for confirmation before moving to the next sub-agent.**
18905
19163
 
@@ -18918,15 +19176,15 @@ Use this when the task follows a standard BPO (Business Process Orchestration) p
18918
19176
  | Section | Content |
18919
19177
  |---------|---------|
18920
19178
  | **Overview** | One-sentence summary of the workflow's purpose and business value |
18921
- | **Steps** | Numbered list of each stage in the pipeline. For each step: which sub-agent handles it, what it does, what tools it uses, and what it produces (output) |
18922
- | **Data Flow** | How data moves between steps. What does each step receive as input? What does it produce and pass to the next step? |
19179
+ | **Steps** | Numbered list of each stage in the pipeline. For each step: which sub-agent handles it, what it does, what tools it uses, what files it reads from \`/project/{project-id}/\`, and what files it writes. |
19180
+ | **Data Flow** | How data moves between steps via files under \`/project/{project-id}/\`. For each step: which files it reads as input and which files it writes as output. |
18923
19181
  | **Logic & Conditions** | Any conditional branching, decision points, retry logic, or exception handling. When does the workflow take path A vs path B? What happens on failure? |
18924
19182
  | **Tools Used** | Summary table or list of all tools used across sub-agents, grouped by sub-agent |
18925
19183
  | **Expected Input** | What input does the workflow expect from the user? (format, examples) |
18926
19184
  | **Expected Output** | What does the workflow produce at the end? (format, examples) |
18927
19185
 
18928
19186
  **Formatting rules:**
18929
- - Write in **Chinese** if the user communicates in Chinese; otherwise English
19187
+ - Write in **English**
18930
19188
  - Use **Markdown** for formatting (headings, lists, tables, code blocks)
18931
19189
  - Be detailed but concise \u2014 each section should be 1-5 lines
18932
19190
  - Avoid placeholder text like "TODO" or "TBD"
@@ -18938,13 +19196,14 @@ Use this when the task follows a standard BPO (Business Process Orchestration) p
18938
19196
  \u672C\u5DE5\u4F5C\u6D41\u81EA\u52A8\u4ECE\u591A\u4E2A\u6570\u636E\u6E90\u91C7\u96C6\u7ADE\u54C1\u4EF7\u683C\uFF0C\u751F\u6210\u5BF9\u6BD4\u5206\u6790\u62A5\u544A\u5E76\u63A8\u9001\u901A\u77E5\u3002
18939
19197
 
18940
19198
  ## Steps
18941
- 1. **\u6570\u636E\u91C7\u96C6 (data-collector)** \u2014 \u4F7F\u7528 browser \u5DE5\u5177\u722C\u53D6\u6307\u5B9AURL\u7684\u7ADE\u54C1\u4EF7\u683C\u6570\u636E\uFF0C\u8C03\u7528 metrics API \u62C9\u53D6\u5386\u53F2\u4EF7\u683C\u3002\u8F93\u51FA\u539F\u59CB\u4EF7\u683C\u6570\u636E\u96C6 JSON\u3002
18942
- 2. **\u6570\u636E\u6E05\u6D17 (data-cleaner)** \u2014 \u4F7F\u7528 code_eval \u6267\u884C Python \u811A\u672C\u6E05\u6D17\u5F02\u5E38\u503C\u548C\u7F3A\u5931\u503C\uFF0C\u7EDF\u4E00\u8D27\u5E01\u5355\u4F4D\u3002\u8F93\u51FA\u6807\u51C6\u5316\u6570\u636E\u96C6\u3002
18943
- 3. **\u62A5\u544A\u751F\u6210 (report-generator)** \u2014 \u8BFB\u53D6\u6A21\u677F\u6587\u4EF6\uFF0Ccode_eval \u8BA1\u7B97\u6DA8\u8DCC\u5E45\u548C\u8D8B\u52BF\uFF0C\u751F\u6210 Markdown \u5BF9\u6BD4\u62A5\u544A\u3002\u8F93\u51FA\u62A5\u544A\u6587\u4EF6\u8DEF\u5F84\u3002
18944
- 4. **\u901A\u77E5\u63A8\u9001 (notifier)** \u2014 \u8C03\u7528 scheduler \u5B89\u6392\u5B9A\u65F6\u53D1\u9001\uFF0C\u4F7F\u7528 ask_user_to_clarify \u8BF7\u6C42\u53D1\u9001\u786E\u8BA4\u3002\u8F93\u51FA\u53D1\u9001\u7ED3\u679C\u3002
19199
+ 1. **\u6570\u636E\u91C7\u96C6 (data-collector)** \u2014 \u4F7F\u7528 browser \u5DE5\u5177\u722C\u53D6\u6307\u5B9AURL\u7684\u7ADE\u54C1\u4EF7\u683C\u6570\u636E\uFF0C\u8C03\u7528 metrics API \u62C9\u53D6\u5386\u53F2\u4EF7\u683C\u3002\u8F93\u51FA \u2192 \`raw_prices.json\`
19200
+ 2. **\u6570\u636E\u6E05\u6D17 (data-cleaner)** \u2014 \u8BFB\u53D6 \`raw_prices.json\`\uFF0C\u4F7F\u7528 code_eval \u6267\u884C Python \u811A\u672C\u6E05\u6D17\u5F02\u5E38\u503C\u548C\u7F3A\u5931\u503C\uFF0C\u7EDF\u4E00\u8D27\u5E01\u5355\u4F4D\u3002\u8F93\u51FA \u2192 \`cleaned_prices.json\`
19201
+ 3. **\u62A5\u544A\u751F\u6210 (report-generator)** \u2014 \u8BFB\u53D6 \`cleaned_prices.json\` \u548C\u6A21\u677F\u6587\u4EF6\uFF0Ccode_eval \u8BA1\u7B97\u6DA8\u8DCC\u5E45\u548C\u8D8B\u52BF\uFF0C\u751F\u6210 Markdown \u5BF9\u6BD4\u62A5\u544A\u3002\u8F93\u51FA \u2192 \`report.md\`
19202
+ 4. **\u901A\u77E5\u63A8\u9001 (notifier)** \u2014 \u8BFB\u53D6 \`report.md\`\uFF0C\u8C03\u7528 scheduler \u5B89\u6392\u5B9A\u65F6\u53D1\u9001\uFF0C\u4F7F\u7528 ask_user_to_clarify \u8BF7\u6C42\u53D1\u9001\u786E\u8BA4\u3002\u8F93\u51FA\u53D1\u9001\u7ED3\u679C\u3002
18945
19203
 
18946
19204
  ## Data Flow
18947
- \`\u7528\u6237\u8F93\u5165(URL\u5217\u8868 + \u65E5\u671F\u8303\u56F4)\` \u2192 data-collector \u722C\u53D6+\u67E5\u8BE2 \u2192 \u539F\u59CBJSON \u2192 data-cleaner \u6E05\u6D17\u8F6C\u6362 \u2192 \u6807\u51C6\u5316JSON \u2192 report-generator \u5206\u6790\u751F\u6210 \u2192 \u62A5\u544AMarkdown \u2192 notifier \u63A8\u9001
19205
+ All data is shared via files under \`/project/{project-id}/\`:
19206
+ \`\u7528\u6237\u8F93\u5165\` \u2192 data-collector writes \`raw_prices.json\` \u2192 data-cleaner reads \`raw_prices.json\`, writes \`cleaned_prices.json\` \u2192 report-generator reads \`cleaned_prices.json\`, writes \`report.md\` \u2192 notifier reads \`report.md\`, pushes
18948
19207
 
18949
19208
  ## Logic & Conditions
18950
19209
  - data-collector \u722C\u53D6\u5931\u8D25\u65F6\u91CD\u8BD5\u6700\u591A3\u6B21\uFF0C\u95F4\u969430\u79D2
@@ -19105,7 +19364,7 @@ Creates a PROCESSING agent with a business-defined workflow topology. Sub-agents
19105
19364
  \`\`\`typescript
19106
19365
  {
19107
19366
  name: string, // Required. Display name for the orchestrator
19108
- description?: string, // Required for good UX. Comprehensive workflow spec in Markdown (Chinese if user speaks Chinese). Must cover: Overview, Steps (with tools per step), Data Flow, Logic & Conditions, Tools Used, Expected Input, Expected Output. This is the single source of truth displayed in the automation view info popover.
19367
+ description?: string, // Required for good UX. Comprehensive workflow spec in Markdown (English). Must cover: Overview, Steps (with tools per step), Data Flow, Logic & Conditions, Tools Used, Expected Input, Expected Output. This is the single source of truth displayed in the automation view info popover.
19109
19368
  prompt: string, // Required. System prompt \u2014 how to route through the topology
19110
19369
  edges: [{ // Required. At least 1 edge defining the serial workflow chain
19111
19370
  from: string, // Source agent ID. First edge: use orchestrator name as placeholder (tool auto-replaces). Subsequent edges: previous sub-agent ID
@@ -21203,7 +21462,7 @@ var MicrosandboxRemoteProvider = class {
21203
21462
  this.instances.delete(name);
21204
21463
  await this.client.deleteSandbox(name);
21205
21464
  }
21206
- createVolumeFsClient(volumeName) {
21465
+ createVolumeFsClient(volumeName, _pathPrefix) {
21207
21466
  return {
21208
21467
  read: (path3) => this.client.volumeFsRead(volumeName, path3),
21209
21468
  write: (path3, content) => this.client.volumeFsWrite(volumeName, path3, content),
@@ -21260,50 +21519,84 @@ var MicrosandboxRemoteProvider = class {
21260
21519
  import { SandboxClient as SandboxClient23 } from "@agent-infra/sandbox";
21261
21520
 
21262
21521
  // src/sandbox_lattice/RemoteSandboxInstance.ts
21522
+ function extractFetcherError(error) {
21523
+ if (typeof error === "string") {
21524
+ return error;
21525
+ }
21526
+ if (error && typeof error === "object") {
21527
+ const e = error;
21528
+ if (typeof e.reason === "string") {
21529
+ switch (e.reason) {
21530
+ case "status-code":
21531
+ return `HTTP ${e.statusCode}: ${JSON.stringify(e.body)}`;
21532
+ case "non-json":
21533
+ return `HTTP ${e.statusCode}: ${e.rawBody}`;
21534
+ case "timeout":
21535
+ return "Request timed out";
21536
+ case "unknown":
21537
+ return typeof e.errorMessage === "string" ? e.errorMessage : "Unknown error";
21538
+ }
21539
+ }
21540
+ if (typeof e.message === "string") {
21541
+ return e.message;
21542
+ }
21543
+ if (typeof e.error === "string") {
21544
+ return e.error;
21545
+ }
21546
+ return JSON.stringify(error);
21547
+ }
21548
+ return String(error);
21549
+ }
21263
21550
  var RemoteSandboxInstance = class {
21264
- constructor(name, client) {
21551
+ constructor(name, client, workspace) {
21265
21552
  this.client = client;
21553
+ this.workspace = workspace;
21266
21554
  this.file = {
21267
21555
  readFile: async (file) => {
21268
- const result = await this.client.file.readFile({ file });
21556
+ const resolved = this.resolvePath(file);
21557
+ const result = await this.client.file.readFile({ file: resolved });
21269
21558
  if (!result.ok) {
21270
- throw new Error(String(result.error));
21559
+ throw new Error(`readFile failed: ${extractFetcherError(result.error)}`);
21271
21560
  }
21272
21561
  return { content: result.body.data?.content ?? "" };
21273
21562
  },
21274
21563
  writeFile: async (file, content) => {
21275
- const result = await this.client.file.writeFile({ file, content });
21564
+ const resolved = this.resolvePath(file);
21565
+ const result = await this.client.file.writeFile({ file: resolved, content });
21276
21566
  if (!result.ok) {
21277
- throw new Error(String(result.error));
21567
+ throw new Error(`writeFile failed: ${extractFetcherError(result.error)}`);
21278
21568
  }
21279
21569
  },
21280
21570
  listPath: async (path3, options) => {
21571
+ const resolved = this.resolvePath(path3);
21281
21572
  const result = await this.client.file.listPath({
21282
- path: path3,
21573
+ path: resolved,
21283
21574
  recursive: options?.recursive ?? false
21284
21575
  });
21285
21576
  if (!result.ok) {
21286
- throw new Error(String(result.error));
21577
+ throw new Error(`listPath failed: ${extractFetcherError(result.error)}`);
21287
21578
  }
21288
21579
  const files = (result.body.data?.files || []).map((f) => ({
21289
21580
  path: f.path,
21290
- is_dir: f.is_dir ?? false,
21291
- size: f.size,
21581
+ is_dir: f.is_dir === true || f.size === null,
21582
+ size: f.size ?? void 0,
21292
21583
  modified_at: f.modified_at
21293
21584
  }));
21294
21585
  return { files };
21295
21586
  },
21296
21587
  findFiles: async (path3, glob) => {
21297
- const result = await this.client.file.findFiles({ path: path3, glob });
21588
+ const resolved = this.resolvePath(path3);
21589
+ const result = await this.client.file.findFiles({ path: resolved, glob });
21298
21590
  if (!result.ok) {
21299
- throw new Error(String(result.error));
21591
+ throw new Error(`findFiles failed: ${extractFetcherError(result.error)}`);
21300
21592
  }
21301
21593
  return { files: result.body.data?.files || [] };
21302
21594
  },
21303
21595
  searchInFile: async (file, regex) => {
21304
- const result = await this.client.file.searchInFile({ file, regex });
21596
+ const resolved = this.resolvePath(file);
21597
+ const result = await this.client.file.searchInFile({ file: resolved, regex });
21305
21598
  if (!result.ok) {
21306
- throw new Error(String(result.error));
21599
+ throw new Error(`searchInFile failed: ${extractFetcherError(result.error)}`);
21307
21600
  }
21308
21601
  return {
21309
21602
  matches: result.body.data?.matches || [],
@@ -21311,30 +21604,33 @@ var RemoteSandboxInstance = class {
21311
21604
  };
21312
21605
  },
21313
21606
  strReplaceEditor: async (params) => {
21607
+ const resolved = this.resolvePath(params.path);
21314
21608
  const result = await this.client.file.strReplaceEditor({
21315
21609
  command: params.command,
21316
- path: params.path,
21610
+ path: resolved,
21317
21611
  old_str: params.old_str,
21318
21612
  new_str: params.new_str,
21319
21613
  replace_mode: params.replace_mode
21320
21614
  });
21321
21615
  if (!result.ok) {
21322
- throw new Error(String(result.error));
21616
+ throw new Error(`strReplaceEditor failed: ${extractFetcherError(result.error)}`);
21323
21617
  }
21324
21618
  },
21325
21619
  uploadFile: async (params) => {
21620
+ const resolved = this.resolvePath(params.file);
21326
21621
  const result = await this.client.file.uploadFile({
21327
21622
  file: params.data,
21328
- path: params.file
21623
+ path: resolved
21329
21624
  });
21330
21625
  if (!result.ok) {
21331
- throw new Error(String(result.error));
21626
+ throw new Error(`uploadFile failed: ${extractFetcherError(result.error)}`);
21332
21627
  }
21333
21628
  },
21334
21629
  downloadFile: async (params) => {
21335
- const result = await this.client.file.downloadFile({ path: params.file });
21630
+ const resolved = this.resolvePath(params.file);
21631
+ const result = await this.client.file.downloadFile({ path: resolved });
21336
21632
  if (!result.ok) {
21337
- throw new Error(String(result.error));
21633
+ throw new Error(`downloadFile failed: ${extractFetcherError(result.error)}`);
21338
21634
  }
21339
21635
  const buffer2 = await result.body.arrayBuffer();
21340
21636
  return Buffer.from(buffer2);
@@ -21344,11 +21640,11 @@ var RemoteSandboxInstance = class {
21344
21640
  execCommand: async (params) => {
21345
21641
  const result = await this.client.shell.execCommand({
21346
21642
  command: params.command,
21347
- exec_dir: params.exec_dir,
21643
+ exec_dir: params.exec_dir ? this.resolvePath(params.exec_dir) : void 0,
21348
21644
  timeout: params.timeout
21349
21645
  });
21350
21646
  if (!result.ok) {
21351
- throw new Error(String(result.error));
21647
+ throw new Error(`execCommand failed: ${extractFetcherError(result.error)}`);
21352
21648
  }
21353
21649
  return {
21354
21650
  output: result.body.data?.output ?? "",
@@ -21358,6 +21654,15 @@ var RemoteSandboxInstance = class {
21358
21654
  };
21359
21655
  this.name = name;
21360
21656
  }
21657
+ resolvePath(file) {
21658
+ if (file.startsWith(this.workspace)) {
21659
+ return file;
21660
+ }
21661
+ if (!file.startsWith("/")) {
21662
+ return `${this.workspace}/${file}`;
21663
+ }
21664
+ return `${this.workspace}${file}`;
21665
+ }
21361
21666
  async start() {
21362
21667
  }
21363
21668
  async stop() {
@@ -21378,23 +21683,58 @@ var RemoteSandboxInstance = class {
21378
21683
  };
21379
21684
 
21380
21685
  // src/sandbox_lattice/providers/RemoteSandboxProvider.ts
21686
+ var DEFAULT_WORKSPACE = "/home/gem";
21381
21687
  var RemoteSandboxProvider = class {
21382
21688
  constructor(config) {
21383
21689
  this.config = config;
21384
21690
  this.instances = /* @__PURE__ */ new Map();
21691
+ this.creating = /* @__PURE__ */ new Map();
21692
+ this.workspace = DEFAULT_WORKSPACE;
21693
+ this.workspaceResolved = false;
21385
21694
  this.client = new SandboxClient23({
21386
21695
  baseUrl: config.baseURL,
21387
21696
  environment: ""
21388
21697
  });
21389
21698
  }
21699
+ async resolveWorkspace() {
21700
+ if (this.workspaceResolved) {
21701
+ return this.workspace;
21702
+ }
21703
+ try {
21704
+ const result = await this.client.sandbox.getContext();
21705
+ if (result.ok && result.body.home_dir) {
21706
+ this.workspace = result.body.home_dir;
21707
+ }
21708
+ } catch {
21709
+ }
21710
+ this.workspaceResolved = true;
21711
+ return this.workspace;
21712
+ }
21390
21713
  async createSandbox(name, _config) {
21391
21714
  const existing = this.instances.get(name);
21392
21715
  if (existing) {
21393
21716
  return existing;
21394
21717
  }
21395
- const instance = new RemoteSandboxInstance(name, this.client);
21396
- this.instances.set(name, instance);
21397
- return instance;
21718
+ const inFlight = this.creating.get(name);
21719
+ if (inFlight) {
21720
+ return inFlight;
21721
+ }
21722
+ const creation = (async () => {
21723
+ const workspace = await this.resolveWorkspace();
21724
+ const instance = new RemoteSandboxInstance(name, this.client, workspace);
21725
+ this.instances.set(name, instance);
21726
+ return instance;
21727
+ })();
21728
+ this.creating.set(name, creation);
21729
+ creation.then(
21730
+ () => {
21731
+ this.creating.delete(name);
21732
+ },
21733
+ () => {
21734
+ this.creating.delete(name);
21735
+ }
21736
+ );
21737
+ return creation;
21398
21738
  }
21399
21739
  async getSandbox(name) {
21400
21740
  const instance = this.instances.get(name);
@@ -21404,9 +21744,7 @@ var RemoteSandboxProvider = class {
21404
21744
  return instance;
21405
21745
  }
21406
21746
  async stopSandbox(name) {
21407
- const instance = this.instances.get(name);
21408
- if (instance) {
21409
- }
21747
+ this.instances.delete(name);
21410
21748
  }
21411
21749
  async deleteSandbox(name) {
21412
21750
  this.instances.delete(name);
@@ -21414,6 +21752,82 @@ var RemoteSandboxProvider = class {
21414
21752
  async listSandboxes() {
21415
21753
  return Array.from(this.instances.values());
21416
21754
  }
21755
+ createVolumeFsClient(_volumeName, pathPrefix) {
21756
+ const workspace = this.workspace;
21757
+ const resolve3 = (p) => {
21758
+ if (!p || p === "/") {
21759
+ if (pathPrefix) {
21760
+ return `${workspace}${pathPrefix}`;
21761
+ }
21762
+ return workspace;
21763
+ }
21764
+ if (p.startsWith(workspace)) {
21765
+ return p;
21766
+ }
21767
+ if (p.startsWith("/")) {
21768
+ return `${workspace}${p}`;
21769
+ }
21770
+ if (pathPrefix) {
21771
+ const mountDir = pathPrefix.replace(/^\//, "");
21772
+ return `${workspace}/${mountDir}/${p}`;
21773
+ }
21774
+ return `${workspace}/${p}`;
21775
+ };
21776
+ return {
21777
+ read: async (path3) => {
21778
+ const resolved = resolve3(path3);
21779
+ const result = await this.client.file.readFile({ file: resolved });
21780
+ if (!result.ok) {
21781
+ throw new Error(`Volume read failed: ${extractFetcherError(result.error)}`);
21782
+ }
21783
+ return result.body.data?.content ?? "";
21784
+ },
21785
+ write: async (path3, content) => {
21786
+ const resolved = resolve3(path3);
21787
+ const result = await this.client.file.writeFile({ file: resolved, content });
21788
+ if (!result.ok) {
21789
+ throw new Error(`Volume write failed: ${extractFetcherError(result.error)}`);
21790
+ }
21791
+ },
21792
+ list: async (path3) => {
21793
+ const resolved = resolve3(path3);
21794
+ const result = await this.client.file.listPath({
21795
+ path: resolved,
21796
+ recursive: false
21797
+ });
21798
+ if (!result.ok) {
21799
+ throw new Error(`Volume list failed: ${extractFetcherError(result.error)}`);
21800
+ }
21801
+ const entries = (result.body.data?.files || []).map((f) => ({
21802
+ path: f.path.startsWith(workspace) ? f.path.slice(workspace.length) : f.path,
21803
+ kind: f.is_dir === true || f.size === null ? "directory" : "file",
21804
+ size: f.size ?? 0,
21805
+ mode: 0,
21806
+ modified: f.modified_at ?? null
21807
+ }));
21808
+ return entries;
21809
+ },
21810
+ readRaw: async (path3) => {
21811
+ const resolved = resolve3(path3);
21812
+ const result = await this.client.file.downloadFile({ path: resolved });
21813
+ if (!result.ok) {
21814
+ throw new Error(`Volume download failed: ${extractFetcherError(result.error)}`);
21815
+ }
21816
+ const buffer2 = await result.body.arrayBuffer();
21817
+ return Buffer.from(buffer2);
21818
+ },
21819
+ writeRaw: async (path3, data) => {
21820
+ const resolved = resolve3(path3);
21821
+ const result = await this.client.file.uploadFile({
21822
+ file: data,
21823
+ path: resolved
21824
+ });
21825
+ if (!result.ok) {
21826
+ throw new Error(`Volume upload failed: ${extractFetcherError(result.error)}`);
21827
+ }
21828
+ }
21829
+ };
21830
+ }
21417
21831
  };
21418
21832
 
21419
21833
  // src/sandbox_lattice/providers/E2BProvider.ts
@@ -22054,6 +22468,7 @@ export {
22054
22468
  MicrosandboxRemoteProvider,
22055
22469
  MicrosandboxServiceClient,
22056
22470
  ModelLatticeManager,
22471
+ MysqlDatabase,
22057
22472
  PinoLoggerClient,
22058
22473
  PostgresDatabase,
22059
22474
  PrometheusClient,
@@ -22118,6 +22533,7 @@ export {
22118
22533
  ensureBuiltinAgentsForTenant,
22119
22534
  eventBus,
22120
22535
  event_bus_default as eventBusDefault,
22536
+ extractFetcherError,
22121
22537
  fileDataToString,
22122
22538
  formatContentWithLineNumbers,
22123
22539
  formatGrepMatches,