@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.js CHANGED
@@ -79,6 +79,7 @@ __export(index_exports, {
79
79
  MicrosandboxRemoteProvider: () => MicrosandboxRemoteProvider,
80
80
  MicrosandboxServiceClient: () => MicrosandboxServiceClient,
81
81
  ModelLatticeManager: () => ModelLatticeManager,
82
+ MysqlDatabase: () => MysqlDatabase,
82
83
  PinoLoggerClient: () => PinoLoggerClient,
83
84
  PostgresDatabase: () => PostgresDatabase,
84
85
  PrometheusClient: () => PrometheusClient,
@@ -143,6 +144,7 @@ __export(index_exports, {
143
144
  ensureBuiltinAgentsForTenant: () => ensureBuiltinAgentsForTenant,
144
145
  eventBus: () => eventBus,
145
146
  eventBusDefault: () => event_bus_default,
147
+ extractFetcherError: () => extractFetcherError,
146
148
  fileDataToString: () => fileDataToString,
147
149
  formatContentWithLineNumbers: () => formatContentWithLineNumbers,
148
150
  formatGrepMatches: () => formatGrepMatches,
@@ -3284,6 +3286,168 @@ var PostgresDatabase = class {
3284
3286
  }
3285
3287
  }
3286
3288
  };
3289
+ var MysqlDatabase = class {
3290
+ constructor(config) {
3291
+ // mysql2.Pool
3292
+ this.connected = false;
3293
+ this.config = config;
3294
+ }
3295
+ async connect() {
3296
+ if (this.connected && this.pool) return;
3297
+ try {
3298
+ const mysql = await import("mysql2/promise");
3299
+ const poolConfig = this.config.connectionString ? { uri: this.config.connectionString } : {
3300
+ host: this.config.host || "localhost",
3301
+ port: this.config.port || 3306,
3302
+ database: this.config.database,
3303
+ user: this.config.user,
3304
+ password: this.config.password,
3305
+ ssl: this.config.ssl ? { rejectUnauthorized: false } : void 0
3306
+ };
3307
+ this.pool = mysql.createPool({
3308
+ ...poolConfig,
3309
+ waitForConnections: true,
3310
+ connectionLimit: 10,
3311
+ maxIdle: 10,
3312
+ idleTimeout: 6e4,
3313
+ queueLimit: 0,
3314
+ enableKeepAlive: true,
3315
+ keepAliveInitialDelay: 0
3316
+ });
3317
+ const connection = await this.pool.getConnection();
3318
+ try {
3319
+ await connection.query("SELECT 1");
3320
+ } finally {
3321
+ connection.release();
3322
+ }
3323
+ this.connected = true;
3324
+ } catch (error) {
3325
+ this.connected = false;
3326
+ throw new Error(`Failed to connect to MySQL: ${error}`);
3327
+ }
3328
+ }
3329
+ async disconnect() {
3330
+ if (this.pool) {
3331
+ try {
3332
+ await this.pool.end();
3333
+ } catch (error) {
3334
+ console.warn("Warning: Error closing MySQL pool:", error);
3335
+ } finally {
3336
+ this.pool = null;
3337
+ this.connected = false;
3338
+ }
3339
+ }
3340
+ }
3341
+ async listTables() {
3342
+ await this.ensureConnected();
3343
+ const query = `
3344
+ SELECT TABLE_NAME, TABLE_SCHEMA
3345
+ FROM information_schema.TABLES
3346
+ WHERE TABLE_SCHEMA NOT IN ('mysql', 'information_schema', 'performance_schema', 'sys')
3347
+ AND TABLE_SCHEMA = DATABASE()
3348
+ AND TABLE_TYPE = 'BASE TABLE'
3349
+ ORDER BY TABLE_SCHEMA, TABLE_NAME
3350
+ `;
3351
+ const [rows] = await this.pool.query(query);
3352
+ return rows.map((row) => ({
3353
+ name: row.TABLE_NAME,
3354
+ schema: row.TABLE_SCHEMA
3355
+ }));
3356
+ }
3357
+ async getTableInfo(tables) {
3358
+ await this.ensureConnected();
3359
+ const schemas = [];
3360
+ for (const tableName of tables) {
3361
+ const rawName = tableName.includes(".") ? tableName.split(".").pop() : tableName;
3362
+ const columnQuery = `
3363
+ SELECT
3364
+ c.COLUMN_NAME,
3365
+ c.DATA_TYPE,
3366
+ c.IS_NULLABLE,
3367
+ c.COLUMN_DEFAULT
3368
+ FROM information_schema.COLUMNS c
3369
+ WHERE c.TABLE_NAME = ?
3370
+ AND c.TABLE_SCHEMA = DATABASE()
3371
+ ORDER BY c.ORDINAL_POSITION
3372
+ `;
3373
+ const pkQuery = `
3374
+ SELECT k.COLUMN_NAME
3375
+ FROM information_schema.TABLE_CONSTRAINTS t
3376
+ JOIN information_schema.KEY_COLUMN_USAGE k
3377
+ ON t.CONSTRAINT_NAME = k.CONSTRAINT_NAME
3378
+ AND t.TABLE_SCHEMA = k.TABLE_SCHEMA
3379
+ AND t.TABLE_NAME = k.TABLE_NAME
3380
+ WHERE t.CONSTRAINT_TYPE = 'PRIMARY KEY'
3381
+ AND t.TABLE_NAME = ?
3382
+ AND t.TABLE_SCHEMA = DATABASE()
3383
+ `;
3384
+ const fkQuery = `
3385
+ SELECT
3386
+ k.COLUMN_NAME,
3387
+ k.REFERENCED_TABLE_NAME,
3388
+ k.REFERENCED_COLUMN_NAME
3389
+ FROM information_schema.KEY_COLUMN_USAGE k
3390
+ WHERE k.TABLE_NAME = ?
3391
+ AND k.TABLE_SCHEMA = DATABASE()
3392
+ AND k.REFERENCED_TABLE_NAME IS NOT NULL
3393
+ `;
3394
+ const [columnRows] = await this.pool.query(columnQuery, [rawName]);
3395
+ const [pkRows] = await this.pool.query(pkQuery, [rawName]);
3396
+ const [fkRows] = await this.pool.query(fkQuery, [rawName]);
3397
+ const pkColumns = new Set(pkRows.map((r) => r.COLUMN_NAME));
3398
+ const fkMap = /* @__PURE__ */ new Map();
3399
+ for (const row of fkRows) {
3400
+ fkMap.set(row.COLUMN_NAME, {
3401
+ foreignTable: row.REFERENCED_TABLE_NAME,
3402
+ foreignColumn: row.REFERENCED_COLUMN_NAME
3403
+ });
3404
+ }
3405
+ const columns = columnRows.map((row) => {
3406
+ const fkRef = fkMap.get(row.COLUMN_NAME);
3407
+ return {
3408
+ name: row.COLUMN_NAME,
3409
+ type: row.DATA_TYPE,
3410
+ nullable: row.IS_NULLABLE === "YES",
3411
+ default: row.COLUMN_DEFAULT,
3412
+ isPrimaryKey: pkColumns.has(row.COLUMN_NAME),
3413
+ isForeignKey: fkRef !== void 0,
3414
+ foreignKeyRef: fkRef ? `${fkRef.foreignTable}.${fkRef.foreignColumn}` : void 0
3415
+ };
3416
+ });
3417
+ let sampleRows = [];
3418
+ try {
3419
+ const sampleQuery = `SELECT * FROM \`${rawName}\` LIMIT 3`;
3420
+ const [sampleResult] = await this.pool.query(sampleQuery);
3421
+ sampleRows = sampleResult;
3422
+ } catch {
3423
+ }
3424
+ schemas.push({
3425
+ tableName,
3426
+ columns,
3427
+ sampleRows
3428
+ });
3429
+ }
3430
+ return schemas;
3431
+ }
3432
+ async executeQuery(query) {
3433
+ await this.ensureConnected();
3434
+ const [rows, fields] = await this.pool.query(query);
3435
+ const isSelectResult = Array.isArray(rows);
3436
+ return {
3437
+ rows: isSelectResult ? rows : [],
3438
+ rowCount: isSelectResult ? rows.length : rows.affectedRows || 0,
3439
+ fields: fields?.map((f) => f.name)
3440
+ };
3441
+ }
3442
+ getDatabaseType() {
3443
+ return "mysql";
3444
+ }
3445
+ async ensureConnected() {
3446
+ if (!this.connected) {
3447
+ await this.connect();
3448
+ }
3449
+ }
3450
+ };
3287
3451
  var SqlDatabaseManager = class _SqlDatabaseManager {
3288
3452
  constructor() {
3289
3453
  this.databases = /* @__PURE__ */ new Map();
@@ -3323,7 +3487,8 @@ var SqlDatabaseManager = class _SqlDatabaseManager {
3323
3487
  database = new PostgresDatabase(config);
3324
3488
  break;
3325
3489
  case "mysql":
3326
- throw new Error("MySQL support not yet implemented");
3490
+ database = new MysqlDatabase(config);
3491
+ break;
3327
3492
  case "sqlite":
3328
3493
  throw new Error("SQLite support not yet implemented");
3329
3494
  default:
@@ -5858,7 +6023,7 @@ var SandboxLatticeManager = class _SandboxLatticeManager extends BaseLatticeMana
5858
6023
  const tenantId = config.tenantId ?? "default";
5859
6024
  const mapping = this._resolveVolumeForPath(config, tenantId, filePath);
5860
6025
  if (!mapping) return null;
5861
- const client = provider.createVolumeFsClient(mapping.volumeName);
6026
+ const client = provider.createVolumeFsClient(mapping.volumeName, mapping.pathPrefix);
5862
6027
  return new VolumeFilesystem(stripPrefixClient(client, mapping.pathPrefix), mapping.pathPrefix);
5863
6028
  }
5864
6029
  _resolveVolumeForPath(config, tenantId, filePath) {
@@ -19070,10 +19235,105 @@ Use this when the task follows a standard BPO (Business Process Orchestration) p
19070
19235
 
19071
19236
  **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.
19072
19237
 
19238
+ **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:
19239
+
19240
+ **Step 1: Construct SO Data Structure**
19241
+ Build the JSON payload. For each DocumentLines item:
19242
+ - Include \`WarehouseCode\` ONLY if sku.json provides a value; if missing or empty, OMIT the field entirely
19243
+ - Generate \`Comments\` dynamically based on context (customer name, PO reference, special instructions)
19244
+
19245
+ \`\`\`json
19246
+ {
19247
+ "DocObjectCode": "17",
19248
+ "DocType": "dDocument_Items",
19249
+ "CardCode": "C001",
19250
+ "DocDate": "current date",
19251
+ "DocDueDate": "delivery date",
19252
+ "TaxDate": "current date",
19253
+ "U_YWLX": "Modern Trade",
19254
+ "Comments": "SO for Kaimay Retail - PO ref: PO-2024-001234",
19255
+ "DocumentLines": [
19256
+ {
19257
+ "ItemCode": "SKU001",
19258
+ "Quantity": 240,
19259
+ "UnitPrice": 15.84
19260
+ }
19261
+ ]
19262
+ }
19263
+ \`\`\`
19264
+
19265
+ Field mapping rules:
19266
+ | SAP Field | Data Source |
19267
+ |-----------|-------------|
19268
+ | CardCode | customer.json \u2192 CardCode |
19269
+ | DocDate | Current date (yyyy-MM-dd) |
19270
+ | DocDueDate | extracted.json \u2192 delivery_date |
19271
+ | TaxDate | Current date (yyyy-MM-dd) |
19272
+ | U_YWLX | customer.json \u2192 SalesType |
19273
+ | ItemCode | sku.json \u2192 ItemCode |
19274
+ | Quantity | extracted.json \u2192 Quantity |
19275
+ | WarehouseCode | sku.json \u2192 WarehouseCode (OMIT if missing/empty) |
19276
+ | UnitPrice | price.json \u2192 UnitPrice |
19277
+ | Comments | AI-generated based on customer name, PO reference, and context |
19278
+
19279
+ **Step 2: Call SAP API to Create Draft (MUST EXECUTE)**
19280
+ Use the \`sap_api_call\` tool with EXACTLY these parameters:
19281
+ - **Method**: POST
19282
+ - **Endpoint**: \`Drafts\` (ONLY allowed endpoint; do NOT call SalesOrders or any other endpoint)
19283
+ - **Body**: The JSON constructed in Step 1
19284
+
19285
+ CRITICAL: You MUST call sap_api_call BEFORE proceeding to Step 3. This step is NOT optional.
19286
+
19287
+ If the API call fails:
19288
+ - Log the error details
19289
+ - STOP the process
19290
+ - Report the failure to the user with the error message
19291
+ - Do NOT proceed to Step 3
19292
+
19293
+ **Step 3: Present Draft for User Review (MUST EXECUTE AFTER Step 2)**
19294
+ ONLY after the SAP API call in Step 2 succeeds, use the \`ask_user_to_clarify\` tool to present the complete SO summary.
19295
+
19296
+ Message format (STRICT \u2014 no emoji, no summary, field names match Service Layer):
19297
+
19298
+ === Sales Order Draft ===
19299
+
19300
+ DocEntry: {DocEntry}
19301
+ DocNum: {DocNum}
19302
+ DocDate: {DocDate}
19303
+ DocDueDate: {DocDueDate}
19304
+ CardCode: {CardCode}
19305
+ CardName: {CardName}
19306
+ U_YWLX: {SalesType}
19307
+ Comments: {Comments}
19308
+
19309
+ DocumentLines:
19310
+ LineNum | ItemCode | Quantity | WarehouseCode | UnitPrice | LineTotal
19311
+ 0 | {ItemCode} | {Quantity} | {WarehouseCode} | {UnitPrice} | {LineTotal}
19312
+ 1 | {ItemCode} | {Quantity} | {WarehouseCode} | {UnitPrice} | {LineTotal}
19313
+ ...
19314
+
19315
+ DocTotal: {DocTotal}
19316
+
19317
+ Warnings:
19318
+ - {warning1}
19319
+ - {warning2}
19320
+
19321
+ Options:
19322
+ [Confirm generating draft]
19323
+ [Cancel, do not generate]
19324
+
19325
+ **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.
19326
+
19327
+ Rules:
19328
+ 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
19329
+ 2. Each sub-agent reads input from \`/project/{project-id}/\` and writes output files into the same directory
19330
+ 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\`)
19331
+ 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
19332
+
19073
19333
  **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.
19074
19334
 
19075
19335
  **Step 3: Design each sub-agent** as a **deep_agent** type (one at a time). For each:
19076
- - Responsibility, system prompt, middleware, input/output contract
19336
+ - Responsibility, system prompt, middleware, file input/output contract (which files from \`/project/{project-id}/\` it reads, which files it writes)
19077
19337
  - Deep agents have built-in file capabilities (ls, read, write, edit, glob, grep), so do NOT add filesystem middleware.
19078
19338
  - **Ask for confirmation before moving to the next sub-agent.**
19079
19339
 
@@ -19092,15 +19352,15 @@ Use this when the task follows a standard BPO (Business Process Orchestration) p
19092
19352
  | Section | Content |
19093
19353
  |---------|---------|
19094
19354
  | **Overview** | One-sentence summary of the workflow's purpose and business value |
19095
- | **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) |
19096
- | **Data Flow** | How data moves between steps. What does each step receive as input? What does it produce and pass to the next step? |
19355
+ | **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. |
19356
+ | **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. |
19097
19357
  | **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? |
19098
19358
  | **Tools Used** | Summary table or list of all tools used across sub-agents, grouped by sub-agent |
19099
19359
  | **Expected Input** | What input does the workflow expect from the user? (format, examples) |
19100
19360
  | **Expected Output** | What does the workflow produce at the end? (format, examples) |
19101
19361
 
19102
19362
  **Formatting rules:**
19103
- - Write in **Chinese** if the user communicates in Chinese; otherwise English
19363
+ - Write in **English**
19104
19364
  - Use **Markdown** for formatting (headings, lists, tables, code blocks)
19105
19365
  - Be detailed but concise \u2014 each section should be 1-5 lines
19106
19366
  - Avoid placeholder text like "TODO" or "TBD"
@@ -19112,13 +19372,14 @@ Use this when the task follows a standard BPO (Business Process Orchestration) p
19112
19372
  \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
19113
19373
 
19114
19374
  ## Steps
19115
- 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
19116
- 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
19117
- 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
19118
- 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
19375
+ 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\`
19376
+ 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\`
19377
+ 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\`
19378
+ 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
19119
19379
 
19120
19380
  ## Data Flow
19121
- \`\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
19381
+ All data is shared via files under \`/project/{project-id}/\`:
19382
+ \`\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
19122
19383
 
19123
19384
  ## Logic & Conditions
19124
19385
  - data-collector \u722C\u53D6\u5931\u8D25\u65F6\u91CD\u8BD5\u6700\u591A3\u6B21\uFF0C\u95F4\u969430\u79D2
@@ -19279,7 +19540,7 @@ Creates a PROCESSING agent with a business-defined workflow topology. Sub-agents
19279
19540
  \`\`\`typescript
19280
19541
  {
19281
19542
  name: string, // Required. Display name for the orchestrator
19282
- 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.
19543
+ 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.
19283
19544
  prompt: string, // Required. System prompt \u2014 how to route through the topology
19284
19545
  edges: [{ // Required. At least 1 edge defining the serial workflow chain
19285
19546
  from: string, // Source agent ID. First edge: use orchestrator name as placeholder (tool auto-replaces). Subsequent edges: previous sub-agent ID
@@ -21375,7 +21636,7 @@ var MicrosandboxRemoteProvider = class {
21375
21636
  this.instances.delete(name);
21376
21637
  await this.client.deleteSandbox(name);
21377
21638
  }
21378
- createVolumeFsClient(volumeName) {
21639
+ createVolumeFsClient(volumeName, _pathPrefix) {
21379
21640
  return {
21380
21641
  read: (path3) => this.client.volumeFsRead(volumeName, path3),
21381
21642
  write: (path3, content) => this.client.volumeFsWrite(volumeName, path3, content),
@@ -21432,50 +21693,84 @@ var MicrosandboxRemoteProvider = class {
21432
21693
  var import_sandbox23 = require("@agent-infra/sandbox");
21433
21694
 
21434
21695
  // src/sandbox_lattice/RemoteSandboxInstance.ts
21696
+ function extractFetcherError(error) {
21697
+ if (typeof error === "string") {
21698
+ return error;
21699
+ }
21700
+ if (error && typeof error === "object") {
21701
+ const e = error;
21702
+ if (typeof e.reason === "string") {
21703
+ switch (e.reason) {
21704
+ case "status-code":
21705
+ return `HTTP ${e.statusCode}: ${JSON.stringify(e.body)}`;
21706
+ case "non-json":
21707
+ return `HTTP ${e.statusCode}: ${e.rawBody}`;
21708
+ case "timeout":
21709
+ return "Request timed out";
21710
+ case "unknown":
21711
+ return typeof e.errorMessage === "string" ? e.errorMessage : "Unknown error";
21712
+ }
21713
+ }
21714
+ if (typeof e.message === "string") {
21715
+ return e.message;
21716
+ }
21717
+ if (typeof e.error === "string") {
21718
+ return e.error;
21719
+ }
21720
+ return JSON.stringify(error);
21721
+ }
21722
+ return String(error);
21723
+ }
21435
21724
  var RemoteSandboxInstance = class {
21436
- constructor(name, client) {
21725
+ constructor(name, client, workspace) {
21437
21726
  this.client = client;
21727
+ this.workspace = workspace;
21438
21728
  this.file = {
21439
21729
  readFile: async (file) => {
21440
- const result = await this.client.file.readFile({ file });
21730
+ const resolved = this.resolvePath(file);
21731
+ const result = await this.client.file.readFile({ file: resolved });
21441
21732
  if (!result.ok) {
21442
- throw new Error(String(result.error));
21733
+ throw new Error(`readFile failed: ${extractFetcherError(result.error)}`);
21443
21734
  }
21444
21735
  return { content: result.body.data?.content ?? "" };
21445
21736
  },
21446
21737
  writeFile: async (file, content) => {
21447
- const result = await this.client.file.writeFile({ file, content });
21738
+ const resolved = this.resolvePath(file);
21739
+ const result = await this.client.file.writeFile({ file: resolved, content });
21448
21740
  if (!result.ok) {
21449
- throw new Error(String(result.error));
21741
+ throw new Error(`writeFile failed: ${extractFetcherError(result.error)}`);
21450
21742
  }
21451
21743
  },
21452
21744
  listPath: async (path3, options) => {
21745
+ const resolved = this.resolvePath(path3);
21453
21746
  const result = await this.client.file.listPath({
21454
- path: path3,
21747
+ path: resolved,
21455
21748
  recursive: options?.recursive ?? false
21456
21749
  });
21457
21750
  if (!result.ok) {
21458
- throw new Error(String(result.error));
21751
+ throw new Error(`listPath failed: ${extractFetcherError(result.error)}`);
21459
21752
  }
21460
21753
  const files = (result.body.data?.files || []).map((f) => ({
21461
21754
  path: f.path,
21462
- is_dir: f.is_dir ?? false,
21463
- size: f.size,
21755
+ is_dir: f.is_dir === true || f.size === null,
21756
+ size: f.size ?? void 0,
21464
21757
  modified_at: f.modified_at
21465
21758
  }));
21466
21759
  return { files };
21467
21760
  },
21468
21761
  findFiles: async (path3, glob) => {
21469
- const result = await this.client.file.findFiles({ path: path3, glob });
21762
+ const resolved = this.resolvePath(path3);
21763
+ const result = await this.client.file.findFiles({ path: resolved, glob });
21470
21764
  if (!result.ok) {
21471
- throw new Error(String(result.error));
21765
+ throw new Error(`findFiles failed: ${extractFetcherError(result.error)}`);
21472
21766
  }
21473
21767
  return { files: result.body.data?.files || [] };
21474
21768
  },
21475
21769
  searchInFile: async (file, regex) => {
21476
- const result = await this.client.file.searchInFile({ file, regex });
21770
+ const resolved = this.resolvePath(file);
21771
+ const result = await this.client.file.searchInFile({ file: resolved, regex });
21477
21772
  if (!result.ok) {
21478
- throw new Error(String(result.error));
21773
+ throw new Error(`searchInFile failed: ${extractFetcherError(result.error)}`);
21479
21774
  }
21480
21775
  return {
21481
21776
  matches: result.body.data?.matches || [],
@@ -21483,30 +21778,33 @@ var RemoteSandboxInstance = class {
21483
21778
  };
21484
21779
  },
21485
21780
  strReplaceEditor: async (params) => {
21781
+ const resolved = this.resolvePath(params.path);
21486
21782
  const result = await this.client.file.strReplaceEditor({
21487
21783
  command: params.command,
21488
- path: params.path,
21784
+ path: resolved,
21489
21785
  old_str: params.old_str,
21490
21786
  new_str: params.new_str,
21491
21787
  replace_mode: params.replace_mode
21492
21788
  });
21493
21789
  if (!result.ok) {
21494
- throw new Error(String(result.error));
21790
+ throw new Error(`strReplaceEditor failed: ${extractFetcherError(result.error)}`);
21495
21791
  }
21496
21792
  },
21497
21793
  uploadFile: async (params) => {
21794
+ const resolved = this.resolvePath(params.file);
21498
21795
  const result = await this.client.file.uploadFile({
21499
21796
  file: params.data,
21500
- path: params.file
21797
+ path: resolved
21501
21798
  });
21502
21799
  if (!result.ok) {
21503
- throw new Error(String(result.error));
21800
+ throw new Error(`uploadFile failed: ${extractFetcherError(result.error)}`);
21504
21801
  }
21505
21802
  },
21506
21803
  downloadFile: async (params) => {
21507
- const result = await this.client.file.downloadFile({ path: params.file });
21804
+ const resolved = this.resolvePath(params.file);
21805
+ const result = await this.client.file.downloadFile({ path: resolved });
21508
21806
  if (!result.ok) {
21509
- throw new Error(String(result.error));
21807
+ throw new Error(`downloadFile failed: ${extractFetcherError(result.error)}`);
21510
21808
  }
21511
21809
  const buffer2 = await result.body.arrayBuffer();
21512
21810
  return Buffer.from(buffer2);
@@ -21516,11 +21814,11 @@ var RemoteSandboxInstance = class {
21516
21814
  execCommand: async (params) => {
21517
21815
  const result = await this.client.shell.execCommand({
21518
21816
  command: params.command,
21519
- exec_dir: params.exec_dir,
21817
+ exec_dir: params.exec_dir ? this.resolvePath(params.exec_dir) : void 0,
21520
21818
  timeout: params.timeout
21521
21819
  });
21522
21820
  if (!result.ok) {
21523
- throw new Error(String(result.error));
21821
+ throw new Error(`execCommand failed: ${extractFetcherError(result.error)}`);
21524
21822
  }
21525
21823
  return {
21526
21824
  output: result.body.data?.output ?? "",
@@ -21530,6 +21828,15 @@ var RemoteSandboxInstance = class {
21530
21828
  };
21531
21829
  this.name = name;
21532
21830
  }
21831
+ resolvePath(file) {
21832
+ if (file.startsWith(this.workspace)) {
21833
+ return file;
21834
+ }
21835
+ if (!file.startsWith("/")) {
21836
+ return `${this.workspace}/${file}`;
21837
+ }
21838
+ return `${this.workspace}${file}`;
21839
+ }
21533
21840
  async start() {
21534
21841
  }
21535
21842
  async stop() {
@@ -21550,23 +21857,58 @@ var RemoteSandboxInstance = class {
21550
21857
  };
21551
21858
 
21552
21859
  // src/sandbox_lattice/providers/RemoteSandboxProvider.ts
21860
+ var DEFAULT_WORKSPACE = "/home/gem";
21553
21861
  var RemoteSandboxProvider = class {
21554
21862
  constructor(config) {
21555
21863
  this.config = config;
21556
21864
  this.instances = /* @__PURE__ */ new Map();
21865
+ this.creating = /* @__PURE__ */ new Map();
21866
+ this.workspace = DEFAULT_WORKSPACE;
21867
+ this.workspaceResolved = false;
21557
21868
  this.client = new import_sandbox23.SandboxClient({
21558
21869
  baseUrl: config.baseURL,
21559
21870
  environment: ""
21560
21871
  });
21561
21872
  }
21873
+ async resolveWorkspace() {
21874
+ if (this.workspaceResolved) {
21875
+ return this.workspace;
21876
+ }
21877
+ try {
21878
+ const result = await this.client.sandbox.getContext();
21879
+ if (result.ok && result.body.home_dir) {
21880
+ this.workspace = result.body.home_dir;
21881
+ }
21882
+ } catch {
21883
+ }
21884
+ this.workspaceResolved = true;
21885
+ return this.workspace;
21886
+ }
21562
21887
  async createSandbox(name, _config) {
21563
21888
  const existing = this.instances.get(name);
21564
21889
  if (existing) {
21565
21890
  return existing;
21566
21891
  }
21567
- const instance = new RemoteSandboxInstance(name, this.client);
21568
- this.instances.set(name, instance);
21569
- return instance;
21892
+ const inFlight = this.creating.get(name);
21893
+ if (inFlight) {
21894
+ return inFlight;
21895
+ }
21896
+ const creation = (async () => {
21897
+ const workspace = await this.resolveWorkspace();
21898
+ const instance = new RemoteSandboxInstance(name, this.client, workspace);
21899
+ this.instances.set(name, instance);
21900
+ return instance;
21901
+ })();
21902
+ this.creating.set(name, creation);
21903
+ creation.then(
21904
+ () => {
21905
+ this.creating.delete(name);
21906
+ },
21907
+ () => {
21908
+ this.creating.delete(name);
21909
+ }
21910
+ );
21911
+ return creation;
21570
21912
  }
21571
21913
  async getSandbox(name) {
21572
21914
  const instance = this.instances.get(name);
@@ -21576,9 +21918,7 @@ var RemoteSandboxProvider = class {
21576
21918
  return instance;
21577
21919
  }
21578
21920
  async stopSandbox(name) {
21579
- const instance = this.instances.get(name);
21580
- if (instance) {
21581
- }
21921
+ this.instances.delete(name);
21582
21922
  }
21583
21923
  async deleteSandbox(name) {
21584
21924
  this.instances.delete(name);
@@ -21586,6 +21926,82 @@ var RemoteSandboxProvider = class {
21586
21926
  async listSandboxes() {
21587
21927
  return Array.from(this.instances.values());
21588
21928
  }
21929
+ createVolumeFsClient(_volumeName, pathPrefix) {
21930
+ const workspace = this.workspace;
21931
+ const resolve3 = (p) => {
21932
+ if (!p || p === "/") {
21933
+ if (pathPrefix) {
21934
+ return `${workspace}${pathPrefix}`;
21935
+ }
21936
+ return workspace;
21937
+ }
21938
+ if (p.startsWith(workspace)) {
21939
+ return p;
21940
+ }
21941
+ if (p.startsWith("/")) {
21942
+ return `${workspace}${p}`;
21943
+ }
21944
+ if (pathPrefix) {
21945
+ const mountDir = pathPrefix.replace(/^\//, "");
21946
+ return `${workspace}/${mountDir}/${p}`;
21947
+ }
21948
+ return `${workspace}/${p}`;
21949
+ };
21950
+ return {
21951
+ read: async (path3) => {
21952
+ const resolved = resolve3(path3);
21953
+ const result = await this.client.file.readFile({ file: resolved });
21954
+ if (!result.ok) {
21955
+ throw new Error(`Volume read failed: ${extractFetcherError(result.error)}`);
21956
+ }
21957
+ return result.body.data?.content ?? "";
21958
+ },
21959
+ write: async (path3, content) => {
21960
+ const resolved = resolve3(path3);
21961
+ const result = await this.client.file.writeFile({ file: resolved, content });
21962
+ if (!result.ok) {
21963
+ throw new Error(`Volume write failed: ${extractFetcherError(result.error)}`);
21964
+ }
21965
+ },
21966
+ list: async (path3) => {
21967
+ const resolved = resolve3(path3);
21968
+ const result = await this.client.file.listPath({
21969
+ path: resolved,
21970
+ recursive: false
21971
+ });
21972
+ if (!result.ok) {
21973
+ throw new Error(`Volume list failed: ${extractFetcherError(result.error)}`);
21974
+ }
21975
+ const entries = (result.body.data?.files || []).map((f) => ({
21976
+ path: f.path.startsWith(workspace) ? f.path.slice(workspace.length) : f.path,
21977
+ kind: f.is_dir === true || f.size === null ? "directory" : "file",
21978
+ size: f.size ?? 0,
21979
+ mode: 0,
21980
+ modified: f.modified_at ?? null
21981
+ }));
21982
+ return entries;
21983
+ },
21984
+ readRaw: async (path3) => {
21985
+ const resolved = resolve3(path3);
21986
+ const result = await this.client.file.downloadFile({ path: resolved });
21987
+ if (!result.ok) {
21988
+ throw new Error(`Volume download failed: ${extractFetcherError(result.error)}`);
21989
+ }
21990
+ const buffer2 = await result.body.arrayBuffer();
21991
+ return Buffer.from(buffer2);
21992
+ },
21993
+ writeRaw: async (path3, data) => {
21994
+ const resolved = resolve3(path3);
21995
+ const result = await this.client.file.uploadFile({
21996
+ file: data,
21997
+ path: resolved
21998
+ });
21999
+ if (!result.ok) {
22000
+ throw new Error(`Volume upload failed: ${extractFetcherError(result.error)}`);
22001
+ }
22002
+ }
22003
+ };
22004
+ }
21589
22005
  };
21590
22006
 
21591
22007
  // src/sandbox_lattice/providers/E2BProvider.ts
@@ -22227,6 +22643,7 @@ function clearEncryptionKeyCache() {
22227
22643
  MicrosandboxRemoteProvider,
22228
22644
  MicrosandboxServiceClient,
22229
22645
  ModelLatticeManager,
22646
+ MysqlDatabase,
22230
22647
  PinoLoggerClient,
22231
22648
  PostgresDatabase,
22232
22649
  PrometheusClient,
@@ -22291,6 +22708,7 @@ function clearEncryptionKeyCache() {
22291
22708
  ensureBuiltinAgentsForTenant,
22292
22709
  eventBus,
22293
22710
  eventBusDefault,
22711
+ extractFetcherError,
22294
22712
  fileDataToString,
22295
22713
  formatContentWithLineNumbers,
22296
22714
  formatGrepMatches,