@ash-cloud/ash-ai 0.1.11 → 0.1.13

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
@@ -199,8 +199,10 @@ var init_types = __esm({
199
199
  // error events
200
200
  FILE: "file",
201
201
  // file_push, file_pull, file_sync (file sync operations)
202
- INPUT: "input"
202
+ INPUT: "input",
203
203
  // user_input (user prompts/messages)
204
+ WEBHOOK: "webhook"
205
+ // webhook_delivery, webhook_failure (outbound webhook events)
204
206
  };
205
207
  }
206
208
  });
@@ -5991,6 +5993,7 @@ var init_sandbox_file_watcher = __esm({
5991
5993
  type,
5992
5994
  relativePath,
5993
5995
  absolutePath,
5996
+ basePath: this.watchPath,
5994
5997
  sessionId: this.sessionId,
5995
5998
  fileSize,
5996
5999
  timestamp: /* @__PURE__ */ new Date()
@@ -6199,6 +6202,7 @@ var init_sandbox_file_watcher = __esm({
6199
6202
  type: "add",
6200
6203
  relativePath: filePath,
6201
6204
  absolutePath: `${this.basePath}/${filePath}`,
6205
+ basePath: this.basePath,
6202
6206
  sessionId: this.sessionId,
6203
6207
  fileSize: info.size,
6204
6208
  timestamp: /* @__PURE__ */ new Date()
@@ -6211,6 +6215,7 @@ var init_sandbox_file_watcher = __esm({
6211
6215
  type: "unlink",
6212
6216
  relativePath: filePath,
6213
6217
  absolutePath: `${this.basePath}/${filePath}`,
6218
+ basePath: this.basePath,
6214
6219
  sessionId: this.sessionId,
6215
6220
  timestamp: /* @__PURE__ */ new Date()
6216
6221
  });
@@ -6400,24 +6405,72 @@ var init_sandbox_file_sync = __esm({
6400
6405
  removeWebhook() {
6401
6406
  this.webhookConfig = void 0;
6402
6407
  }
6408
+ /**
6409
+ * Extract hostname from URL for display (security - don't expose full URL)
6410
+ */
6411
+ extractHostname(url) {
6412
+ try {
6413
+ const urlObj = new URL(url);
6414
+ return urlObj.hostname;
6415
+ } catch {
6416
+ return "unknown";
6417
+ }
6418
+ }
6419
+ /**
6420
+ * Persist webhook delivery event to the session timeline
6421
+ */
6422
+ async persistWebhookDelivery(sessionId, webhookType, result, filePath, operation) {
6423
+ if (!this.eventStorage || !this.webhookConfig) return;
6424
+ try {
6425
+ const now = /* @__PURE__ */ new Date();
6426
+ const sequenceNumber = await this.getNextSequenceNumber(sessionId);
6427
+ const eventData = {
6428
+ webhookType,
6429
+ targetUrl: this.extractHostname(this.webhookConfig.url),
6430
+ success: result.success,
6431
+ statusCode: result.statusCode,
6432
+ error: result.error,
6433
+ durationMs: result.durationMs,
6434
+ retryCount: result.retryCount > 0 ? result.retryCount : void 0,
6435
+ filePath,
6436
+ operation
6437
+ };
6438
+ const eventType = result.success ? "webhook_delivery" : "webhook_failure";
6439
+ const sessionEvent = {
6440
+ eventType,
6441
+ category: EventCategory.WEBHOOK,
6442
+ startedAt: new Date(now.getTime() - result.durationMs),
6443
+ endedAt: now,
6444
+ durationMs: result.durationMs,
6445
+ eventData,
6446
+ sequenceNumber
6447
+ };
6448
+ await this.eventStorage.saveEvents(sessionId, [sessionEvent]);
6449
+ } catch (error) {
6450
+ console.error("[FILE_SYNC] Error saving webhook delivery event to storage:", error);
6451
+ }
6452
+ }
6403
6453
  /**
6404
6454
  * Send a webhook notification
6405
6455
  * @param payload - The webhook payload to send
6456
+ * @param sessionId - Session ID for persisting delivery events
6457
+ * @returns WebhookDeliveryResult with success/failure info
6406
6458
  */
6407
- async sendWebhook(payload) {
6408
- if (!this.webhookConfig) return;
6459
+ async sendWebhook(payload, sessionId) {
6460
+ if (!this.webhookConfig) return null;
6409
6461
  const config = this.webhookConfig;
6410
6462
  const eventType = payload.fileSyncEvent?.operation ?? "file_change";
6411
6463
  if (config.events && config.events.length > 0) {
6412
6464
  const shouldSend = config.events.some(
6413
6465
  (e) => e === eventType || e === "file_change" && payload.event === "file_change"
6414
6466
  );
6415
- if (!shouldSend) return;
6467
+ if (!shouldSend) return null;
6416
6468
  }
6417
6469
  const body = JSON.stringify(payload);
6418
6470
  const timeoutMs = config.timeoutMs ?? 1e4;
6419
6471
  const retries = config.retries ?? 3;
6420
6472
  const isAsync = config.async !== false;
6473
+ const startTime = Date.now();
6421
6474
  const headers = {
6422
6475
  "Content-Type": "application/json",
6423
6476
  "User-Agent": "Ash-FileSync/1.0",
@@ -6439,12 +6492,23 @@ var init_sandbox_file_sync = __esm({
6439
6492
  signal: controller.signal
6440
6493
  });
6441
6494
  clearTimeout(timeoutId);
6495
+ const durationMs = Date.now() - startTime;
6442
6496
  if (!response.ok) {
6443
- throw new Error(`Webhook returned ${response.status}: ${response.statusText}`);
6497
+ const error = `Webhook returned ${response.status}: ${response.statusText}`;
6498
+ if (attempt < retries) {
6499
+ const delay = Math.pow(2, attempt) * 1e3;
6500
+ console.warn(`[FILE_SYNC] Webhook failed (attempt ${attempt + 1}/${retries}), retrying in ${delay}ms: ${error}`);
6501
+ await new Promise((resolve3) => setTimeout(resolve3, delay));
6502
+ return sendWithRetry(attempt + 1);
6503
+ }
6504
+ console.error(`[FILE_SYNC] Webhook failed after ${retries} attempts: ${error}`);
6505
+ return { success: false, statusCode: response.status, error, durationMs, retryCount: attempt };
6444
6506
  }
6445
6507
  console.log(`[FILE_SYNC] Webhook sent successfully to ${config.url}`);
6508
+ return { success: true, statusCode: response.status, durationMs, retryCount: attempt };
6446
6509
  } catch (error) {
6447
6510
  const errorMsg = error instanceof Error ? error.message : String(error);
6511
+ const durationMs = Date.now() - startTime;
6448
6512
  if (attempt < retries) {
6449
6513
  const delay = Math.pow(2, attempt) * 1e3;
6450
6514
  console.warn(`[FILE_SYNC] Webhook failed (attempt ${attempt + 1}/${retries}), retrying in ${delay}ms: ${errorMsg}`);
@@ -6452,15 +6516,27 @@ var init_sandbox_file_sync = __esm({
6452
6516
  return sendWithRetry(attempt + 1);
6453
6517
  }
6454
6518
  console.error(`[FILE_SYNC] Webhook failed after ${retries} attempts: ${errorMsg}`);
6455
- throw error;
6519
+ return { success: false, error: errorMsg, durationMs, retryCount: attempt };
6456
6520
  }
6457
6521
  };
6522
+ const webhookType = payload.event === "file_sync" ? "file_sync" : "file_change";
6523
+ const filePath = payload.fileSyncEvent?.canonicalPath ?? payload.fileChangeEvent?.relativePath;
6524
+ const operation = payload.fileSyncEvent?.operation ?? payload.fileChangeEvent?.type;
6458
6525
  if (isAsync) {
6459
- sendWithRetry(0).catch((error) => {
6526
+ sendWithRetry(0).then(async (result) => {
6527
+ if (sessionId) {
6528
+ await this.persistWebhookDelivery(sessionId, webhookType, result, filePath, operation);
6529
+ }
6530
+ }).catch((error) => {
6460
6531
  console.error("[FILE_SYNC] Async webhook failed:", error);
6461
6532
  });
6533
+ return null;
6462
6534
  } else {
6463
- await sendWithRetry(0);
6535
+ const result = await sendWithRetry(0);
6536
+ if (sessionId) {
6537
+ await this.persistWebhookDelivery(sessionId, webhookType, result, filePath, operation);
6538
+ }
6539
+ return result;
6464
6540
  }
6465
6541
  }
6466
6542
  /**
@@ -6474,7 +6550,7 @@ var init_sandbox_file_sync = __esm({
6474
6550
  metadata: this.webhookConfig?.metadata,
6475
6551
  fileSyncEvent: event
6476
6552
  };
6477
- await this.sendWebhook(payload);
6553
+ await this.sendWebhook(payload, sessionId);
6478
6554
  }
6479
6555
  /**
6480
6556
  * Send a file change event webhook (from watcher)
@@ -6487,7 +6563,7 @@ var init_sandbox_file_sync = __esm({
6487
6563
  metadata: this.webhookConfig?.metadata,
6488
6564
  fileChangeEvent: event
6489
6565
  };
6490
- await this.sendWebhook(payload);
6566
+ await this.sendWebhook(payload, event.sessionId);
6491
6567
  }
6492
6568
  /**
6493
6569
  * Get the next sequence number for a session
@@ -6573,10 +6649,10 @@ var init_sandbox_file_sync = __esm({
6573
6649
  const isTextFile = diff !== void 0 || event.newContent && !event.newContent.slice(0, 8192).includes(0);
6574
6650
  const eventData = {
6575
6651
  operation: event.operation,
6576
- direction: event.direction,
6577
- source: "internal",
6578
- // File sync operations are internal
6579
- filePath: event.filePath,
6652
+ source: event.source,
6653
+ canonicalPath: event.canonicalPath,
6654
+ basePath: event.basePath,
6655
+ sandboxPath: event.sandboxPath,
6580
6656
  fileSize: event.fileSize,
6581
6657
  success: event.success,
6582
6658
  error: event.error,
@@ -6584,7 +6660,7 @@ var init_sandbox_file_sync = __esm({
6584
6660
  isTextFile,
6585
6661
  previousSize: event.previousContent?.length
6586
6662
  };
6587
- const eventType = `file_${event.operation}`;
6663
+ const eventType = event.operation;
6588
6664
  const sessionEvent = {
6589
6665
  eventType,
6590
6666
  category: EventCategory.FILE,
@@ -6607,6 +6683,13 @@ var init_sandbox_file_sync = __esm({
6607
6683
  setSandboxOperations(ops) {
6608
6684
  this.sandboxOps = ops;
6609
6685
  }
6686
+ /**
6687
+ * Get the effective base path for sandbox operations
6688
+ * @param targetPath - Optional override for the base path
6689
+ */
6690
+ getBasePath(targetPath) {
6691
+ return targetPath ?? this.sandboxBasePath;
6692
+ }
6610
6693
  /**
6611
6694
  * Get the full sandbox path for a file
6612
6695
  * @param path - The relative file path
@@ -6614,18 +6697,36 @@ var init_sandbox_file_sync = __esm({
6614
6697
  */
6615
6698
  getSandboxPath(path15, targetPath) {
6616
6699
  const normalizedPath = path15.replace(/^\/+/, "");
6617
- const basePath = targetPath ?? this.sandboxBasePath;
6700
+ const basePath = this.getBasePath(targetPath);
6618
6701
  if (basePath === ".") {
6619
6702
  return normalizedPath;
6620
6703
  }
6621
6704
  return `${basePath}/${normalizedPath}`;
6622
6705
  }
6706
+ /**
6707
+ * Build all path fields for a FileSyncEvent
6708
+ * @param path - The canonical file path (used as S3 key)
6709
+ * @param targetPath - Optional override for the base path
6710
+ */
6711
+ buildPathFields(path15, targetPath) {
6712
+ const normalizedPath = path15.replace(/^\/+/, "");
6713
+ const basePath = this.getBasePath(targetPath);
6714
+ const sandboxPath = this.getSandboxPath(path15, targetPath);
6715
+ return {
6716
+ canonicalPath: normalizedPath,
6717
+ // The logical path (S3 key)
6718
+ basePath,
6719
+ // The prefix used in sandbox
6720
+ sandboxPath
6721
+ // Full computed path in sandbox
6722
+ };
6723
+ }
6623
6724
  /**
6624
6725
  * Push a file: writes to S3, then to sandbox if running
6625
6726
  * @param sessionId - Session ID
6626
6727
  * @param path - File path (stored in S3 and used as relative path in sandbox)
6627
6728
  * @param content - File content
6628
- * @param options - Push options (e.g., targetPath to override sandbox location)
6729
+ * @param options - Push options (e.g., targetPath, source)
6629
6730
  * @param previousContent - Optional previous content for diff computation
6630
6731
  */
6631
6732
  async pushFile(sessionId, path15, content, options, previousContent) {
@@ -6634,13 +6735,16 @@ var init_sandbox_file_sync = __esm({
6634
6735
  s3Written: false,
6635
6736
  sandboxWritten: false
6636
6737
  };
6738
+ const source = options?.source ?? "client_api";
6739
+ const uploadToStorageOp = source === "client_api" ? "client_uploaded_to_ash_storage" : "agent_sandbox_saved_to_ash_storage";
6740
+ const pathFields = this.buildPathFields(path15, options?.targetPath);
6637
6741
  try {
6638
6742
  await this.fileStore.writeFile(sessionId, path15, content);
6639
6743
  result.s3Written = true;
6640
6744
  await this.emitFileEvent(sessionId, {
6641
- operation: "push",
6642
- direction: "to_s3",
6643
- filePath: path15,
6745
+ operation: uploadToStorageOp,
6746
+ source,
6747
+ ...pathFields,
6644
6748
  fileSize: content.length,
6645
6749
  success: true,
6646
6750
  previousContent,
@@ -6651,9 +6755,9 @@ var init_sandbox_file_sync = __esm({
6651
6755
  result.error = `S3 write failed: ${errorMessage}`;
6652
6756
  console.error(`[FILE_SYNC] S3 write failed for session ${sessionId}, path ${path15}:`, error);
6653
6757
  await this.emitFileEvent(sessionId, {
6654
- operation: "push",
6655
- direction: "to_s3",
6656
- filePath: path15,
6758
+ operation: uploadToStorageOp,
6759
+ source,
6760
+ ...pathFields,
6657
6761
  fileSize: content.length,
6658
6762
  success: false,
6659
6763
  error: errorMessage
@@ -6662,13 +6766,12 @@ var init_sandbox_file_sync = __esm({
6662
6766
  }
6663
6767
  if (this.sandboxOps?.isSandboxRunning(sessionId)) {
6664
6768
  try {
6665
- const sandboxPath = this.getSandboxPath(path15, options?.targetPath);
6666
- const writeResult = await this.sandboxOps.writeFile(sessionId, sandboxPath, content);
6769
+ const writeResult = await this.sandboxOps.writeFile(sessionId, pathFields.sandboxPath, content);
6667
6770
  result.sandboxWritten = writeResult.success;
6668
6771
  await this.emitFileEvent(sessionId, {
6669
- operation: "push",
6670
- direction: "to_sandbox",
6671
- filePath: path15,
6772
+ operation: "ash_storage_synced_to_agent_sandbox",
6773
+ source,
6774
+ ...pathFields,
6672
6775
  fileSize: content.length,
6673
6776
  success: writeResult.success,
6674
6777
  error: writeResult.error
@@ -6680,9 +6783,9 @@ var init_sandbox_file_sync = __esm({
6680
6783
  const errorMessage = extractErrorMessage(error);
6681
6784
  console.warn(`[FILE_SYNC] Sandbox write error for ${path15}: ${errorMessage}`);
6682
6785
  await this.emitFileEvent(sessionId, {
6683
- operation: "push",
6684
- direction: "to_sandbox",
6685
- filePath: path15,
6786
+ operation: "ash_storage_synced_to_agent_sandbox",
6787
+ source,
6788
+ ...pathFields,
6686
6789
  fileSize: content.length,
6687
6790
  success: false,
6688
6791
  error: errorMessage
@@ -6710,8 +6813,12 @@ var init_sandbox_file_sync = __esm({
6710
6813
  /**
6711
6814
  * Pull a file from sandbox to S3
6712
6815
  * Reads from sandbox and writes to S3
6816
+ *
6817
+ * @param sessionId - Session ID
6818
+ * @param path - File path (relative to sandbox)
6819
+ * @param options - Pull options (e.g., targetPath to override sandbox location)
6713
6820
  */
6714
- async pullFile(sessionId, path15) {
6821
+ async pullFile(sessionId, path15, options) {
6715
6822
  const result = {
6716
6823
  path: path15,
6717
6824
  content: null,
@@ -6721,15 +6828,15 @@ var init_sandbox_file_sync = __esm({
6721
6828
  result.error = "Sandbox is not running";
6722
6829
  return result;
6723
6830
  }
6831
+ const pathFields = this.buildPathFields(path15, options?.targetPath);
6724
6832
  try {
6725
- const sandboxPath = this.getSandboxPath(path15);
6726
- const readResult = await this.sandboxOps.readFile(sessionId, sandboxPath);
6833
+ const readResult = await this.sandboxOps.readFile(sessionId, pathFields.sandboxPath);
6727
6834
  if (!readResult.success || !readResult.content) {
6728
6835
  result.error = readResult.error ?? "File not found in sandbox";
6729
6836
  await this.emitFileEvent(sessionId, {
6730
- operation: "pull",
6731
- direction: "from_sandbox",
6732
- filePath: path15,
6837
+ operation: "read_from_agent_sandbox",
6838
+ source: "ash_file_sync",
6839
+ ...pathFields,
6733
6840
  success: false,
6734
6841
  error: result.error
6735
6842
  });
@@ -6737,9 +6844,9 @@ var init_sandbox_file_sync = __esm({
6737
6844
  }
6738
6845
  result.content = readResult.content;
6739
6846
  await this.emitFileEvent(sessionId, {
6740
- operation: "pull",
6741
- direction: "from_sandbox",
6742
- filePath: path15,
6847
+ operation: "read_from_agent_sandbox",
6848
+ source: "ash_file_sync",
6849
+ ...pathFields,
6743
6850
  fileSize: readResult.content.length,
6744
6851
  success: true,
6745
6852
  newContent: readResult.content
@@ -6748,9 +6855,9 @@ var init_sandbox_file_sync = __esm({
6748
6855
  const errorMessage = extractErrorMessage(error);
6749
6856
  result.error = `Sandbox read failed: ${errorMessage}`;
6750
6857
  await this.emitFileEvent(sessionId, {
6751
- operation: "pull",
6752
- direction: "from_sandbox",
6753
- filePath: path15,
6858
+ operation: "read_from_agent_sandbox",
6859
+ source: "ash_file_sync",
6860
+ ...pathFields,
6754
6861
  success: false,
6755
6862
  error: errorMessage
6756
6863
  });
@@ -6760,9 +6867,9 @@ var init_sandbox_file_sync = __esm({
6760
6867
  await this.fileStore.writeFile(sessionId, path15, result.content);
6761
6868
  result.s3Written = true;
6762
6869
  await this.emitFileEvent(sessionId, {
6763
- operation: "pull",
6764
- direction: "to_s3",
6765
- filePath: path15,
6870
+ operation: "agent_sandbox_saved_to_ash_storage",
6871
+ source: "ash_file_sync",
6872
+ ...pathFields,
6766
6873
  fileSize: result.content.length,
6767
6874
  success: true,
6768
6875
  newContent: result.content
@@ -6772,9 +6879,9 @@ var init_sandbox_file_sync = __esm({
6772
6879
  result.error = `S3 write failed: ${errorMessage}`;
6773
6880
  console.error(`[FILE_SYNC] S3 write failed in pullFile for session ${sessionId}, path ${path15}:`, error);
6774
6881
  await this.emitFileEvent(sessionId, {
6775
- operation: "pull",
6776
- direction: "to_s3",
6777
- filePath: path15,
6882
+ operation: "agent_sandbox_saved_to_ash_storage",
6883
+ source: "ash_file_sync",
6884
+ ...pathFields,
6778
6885
  fileSize: result.content?.length,
6779
6886
  success: false,
6780
6887
  error: errorMessage
@@ -6816,43 +6923,43 @@ var init_sandbox_file_sync = __esm({
6816
6923
  */
6817
6924
  async deleteFile(sessionId, path15) {
6818
6925
  const result = { s3Deleted: false, sandboxDeleted: false };
6926
+ const pathFields = this.buildPathFields(path15);
6819
6927
  try {
6820
6928
  await this.fileStore.deleteFile(sessionId, path15);
6821
6929
  result.s3Deleted = true;
6822
6930
  await this.emitFileEvent(sessionId, {
6823
- operation: "delete",
6824
- direction: "from_s3",
6825
- filePath: path15,
6931
+ operation: "deleted_from_ash_storage",
6932
+ source: "ash_file_sync",
6933
+ ...pathFields,
6826
6934
  success: true
6827
6935
  });
6828
6936
  } catch (error) {
6829
6937
  const errorMessage = extractErrorMessage(error);
6830
6938
  console.warn(`[FILE_SYNC] S3 delete failed for ${path15}: ${errorMessage}`);
6831
6939
  await this.emitFileEvent(sessionId, {
6832
- operation: "delete",
6833
- direction: "from_s3",
6834
- filePath: path15,
6940
+ operation: "deleted_from_ash_storage",
6941
+ source: "ash_file_sync",
6942
+ ...pathFields,
6835
6943
  success: false,
6836
6944
  error: errorMessage
6837
6945
  });
6838
6946
  }
6839
6947
  if (this.sandboxOps?.isSandboxRunning(sessionId)) {
6840
6948
  try {
6841
- const sandboxPath = this.getSandboxPath(path15);
6842
- console.log(`[FILE_SYNC] Would delete ${sandboxPath} from sandbox`);
6949
+ console.log(`[FILE_SYNC] Would delete ${pathFields.sandboxPath} from sandbox`);
6843
6950
  result.sandboxDeleted = true;
6844
6951
  await this.emitFileEvent(sessionId, {
6845
- operation: "delete",
6846
- direction: "from_sandbox",
6847
- filePath: path15,
6952
+ operation: "deleted_from_agent_sandbox",
6953
+ source: "ash_file_sync",
6954
+ ...pathFields,
6848
6955
  success: true
6849
6956
  });
6850
6957
  } catch (error) {
6851
6958
  const errorMessage = extractErrorMessage(error);
6852
6959
  await this.emitFileEvent(sessionId, {
6853
- operation: "delete",
6854
- direction: "from_sandbox",
6855
- filePath: path15,
6960
+ operation: "deleted_from_agent_sandbox",
6961
+ source: "ash_file_sync",
6962
+ ...pathFields,
6856
6963
  success: false,
6857
6964
  error: errorMessage
6858
6965
  });
@@ -6872,36 +6979,36 @@ var init_sandbox_file_sync = __esm({
6872
6979
  }
6873
6980
  const files = await this.fileStore.listFiles(sessionId);
6874
6981
  for (const file of files) {
6982
+ const pathFields = this.buildPathFields(file.path);
6875
6983
  try {
6876
6984
  const content = await this.fileStore.readFile(sessionId, file.path);
6877
6985
  if (!content) {
6878
6986
  result.errors.push({ path: file.path, error: "File not found in S3" });
6879
6987
  await this.emitFileEvent(sessionId, {
6880
- operation: "sync_to_sandbox",
6881
- direction: "from_s3",
6882
- filePath: file.path,
6988
+ operation: "ash_storage_synced_to_agent_sandbox",
6989
+ source: "ash_file_sync",
6990
+ ...pathFields,
6883
6991
  success: false,
6884
- error: "File not found in S3"
6992
+ error: "File not found in Ash storage"
6885
6993
  });
6886
6994
  continue;
6887
6995
  }
6888
- const sandboxPath = this.getSandboxPath(file.path);
6889
- const writeResult = await this.sandboxOps.writeFile(sessionId, sandboxPath, content);
6996
+ const writeResult = await this.sandboxOps.writeFile(sessionId, pathFields.sandboxPath, content);
6890
6997
  if (writeResult.success) {
6891
6998
  result.fileCount++;
6892
6999
  await this.emitFileEvent(sessionId, {
6893
- operation: "sync_to_sandbox",
6894
- direction: "to_sandbox",
6895
- filePath: file.path,
7000
+ operation: "ash_storage_synced_to_agent_sandbox",
7001
+ source: "ash_file_sync",
7002
+ ...pathFields,
6896
7003
  fileSize: content.length,
6897
7004
  success: true
6898
7005
  });
6899
7006
  } else {
6900
7007
  result.errors.push({ path: file.path, error: writeResult.error ?? "Unknown error" });
6901
7008
  await this.emitFileEvent(sessionId, {
6902
- operation: "sync_to_sandbox",
6903
- direction: "to_sandbox",
6904
- filePath: file.path,
7009
+ operation: "ash_storage_synced_to_agent_sandbox",
7010
+ source: "ash_file_sync",
7011
+ ...pathFields,
6905
7012
  fileSize: content.length,
6906
7013
  success: false,
6907
7014
  error: writeResult.error
@@ -6914,9 +7021,9 @@ var init_sandbox_file_sync = __esm({
6914
7021
  error: errorMessage
6915
7022
  });
6916
7023
  await this.emitFileEvent(sessionId, {
6917
- operation: "sync_to_sandbox",
6918
- direction: "to_sandbox",
6919
- filePath: file.path,
7024
+ operation: "ash_storage_synced_to_agent_sandbox",
7025
+ source: "ash_file_sync",
7026
+ ...pathFields,
6920
7027
  success: false,
6921
7028
  error: errorMessage
6922
7029
  });
@@ -6947,39 +7054,25 @@ var init_sandbox_file_sync = __esm({
6947
7054
  return result;
6948
7055
  }
6949
7056
  }
6950
- for (const filePath of filesToSync) {
7057
+ for (const file of filesToSync) {
7058
+ const pathFields = this.buildPathFields(file);
6951
7059
  try {
6952
- const pullResult = await this.pullFile(sessionId, filePath);
7060
+ const pullResult = await this.pullFile(sessionId, file);
6953
7061
  if (pullResult.s3Written) {
6954
7062
  result.fileCount++;
6955
- await this.emitFileEvent(sessionId, {
6956
- operation: "sync_from_sandbox",
6957
- direction: "to_s3",
6958
- filePath,
6959
- fileSize: pullResult.content?.length,
6960
- success: true,
6961
- newContent: pullResult.content ?? void 0
6962
- });
6963
7063
  } else if (pullResult.error) {
6964
- result.errors.push({ path: filePath, error: pullResult.error });
6965
- await this.emitFileEvent(sessionId, {
6966
- operation: "sync_from_sandbox",
6967
- direction: "to_s3",
6968
- filePath,
6969
- success: false,
6970
- error: pullResult.error
6971
- });
7064
+ result.errors.push({ path: file, error: pullResult.error });
6972
7065
  }
6973
7066
  } catch (error) {
6974
7067
  const errorMessage = extractErrorMessage(error);
6975
7068
  result.errors.push({
6976
- path: filePath,
7069
+ path: file,
6977
7070
  error: errorMessage
6978
7071
  });
6979
7072
  await this.emitFileEvent(sessionId, {
6980
- operation: "sync_from_sandbox",
6981
- direction: "to_s3",
6982
- filePath,
7073
+ operation: "agent_sandbox_saved_to_ash_storage",
7074
+ source: "ash_file_sync",
7075
+ ...pathFields,
6983
7076
  success: false,
6984
7077
  error: errorMessage
6985
7078
  });
@@ -7014,9 +7107,10 @@ var init_sandbox_file_sync = __esm({
7014
7107
  */
7015
7108
  async startWatching(sessionId, options) {
7016
7109
  const opts = { ...this.defaultWatchOptions, ...options };
7110
+ const watchPath = opts.watchPaths?.[0] ?? ".";
7017
7111
  await this.stopWatching(sessionId);
7018
7112
  const handleFileChange = async (event) => {
7019
- console.log(`[FILE_SYNC] File change detected: ${event.type} ${event.relativePath}`);
7113
+ console.log(`[FILE_SYNC] File change detected: ${event.type} ${event.relativePath} (watching from: ${watchPath})`);
7020
7114
  if (this.webhookConfig) {
7021
7115
  this.sendFileChangeWebhook(event);
7022
7116
  }
@@ -7029,7 +7123,7 @@ var init_sandbox_file_sync = __esm({
7029
7123
  }
7030
7124
  if (event.type === "add" || event.type === "change") {
7031
7125
  try {
7032
- const pullResult = await this.pullFile(sessionId, event.relativePath);
7126
+ const pullResult = await this.pullFile(sessionId, event.relativePath, { targetPath: watchPath });
7033
7127
  if (pullResult.s3Written) {
7034
7128
  console.log(`[FILE_SYNC] Auto-synced ${event.relativePath} to S3`);
7035
7129
  } else if (pullResult.error) {
@@ -7060,7 +7154,7 @@ var init_sandbox_file_sync = __esm({
7060
7154
  });
7061
7155
  await watcher.start();
7062
7156
  this.localWatchers.set(sessionId, watcher);
7063
- console.log(`[FILE_SYNC] Started local file watching for session ${sessionId}`);
7157
+ console.log(`[FILE_SYNC] Started local file watching for session ${sessionId} at ${opts.localPath}`);
7064
7158
  } else {
7065
7159
  if (!this.sandboxOps) {
7066
7160
  throw new Error("Sandbox operations not configured. Call setSandboxOperations first.");
@@ -7068,7 +7162,8 @@ var init_sandbox_file_sync = __esm({
7068
7162
  const watcher = new RemoteSandboxFileWatcher({
7069
7163
  sessionId,
7070
7164
  sandboxOps: this.sandboxOps,
7071
- basePath: this.sandboxBasePath,
7165
+ basePath: watchPath,
7166
+ // Use watchPath instead of sandboxBasePath
7072
7167
  pollIntervalMs: opts.pollIntervalMs ?? 2e3,
7073
7168
  ignored: opts.ignored ?? ["**/node_modules/**", "**/.git/**"],
7074
7169
  onFileChange: handleFileChange,
@@ -7078,7 +7173,7 @@ var init_sandbox_file_sync = __esm({
7078
7173
  });
7079
7174
  await watcher.start();
7080
7175
  this.remoteWatchers.set(sessionId, watcher);
7081
- console.log(`[FILE_SYNC] Started remote file watching for session ${sessionId}`);
7176
+ console.log(`[FILE_SYNC] Started remote file watching for session ${sessionId} at path: ${watchPath}`);
7082
7177
  }
7083
7178
  }
7084
7179
  /**
@@ -7391,6 +7486,7 @@ WATCHER_EOF`;
7391
7486
  type: data.type,
7392
7487
  relativePath: data.path.replace(/^\.\//, ""),
7393
7488
  absolutePath: data.path,
7489
+ basePath: this.watchPath,
7394
7490
  sessionId: this.sessionId,
7395
7491
  timestamp: new Date(data.timestamp)
7396
7492
  });
@@ -16213,8 +16309,10 @@ var init_schema = __esm({
16213
16309
  // error events
16214
16310
  "file",
16215
16311
  // file_push, file_pull, file_sync (file sync operations)
16216
- "input"
16312
+ "input",
16217
16313
  // user_input (user prompts/messages)
16314
+ "webhook"
16315
+ // webhook_delivery, webhook_failure (outbound webhook events)
16218
16316
  ]);
16219
16317
  sessions = pgTable(
16220
16318
  "sessions",