@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.cjs CHANGED
@@ -227,8 +227,10 @@ var init_types = __esm({
227
227
  // error events
228
228
  FILE: "file",
229
229
  // file_push, file_pull, file_sync (file sync operations)
230
- INPUT: "input"
230
+ INPUT: "input",
231
231
  // user_input (user prompts/messages)
232
+ WEBHOOK: "webhook"
233
+ // webhook_delivery, webhook_failure (outbound webhook events)
232
234
  };
233
235
  }
234
236
  });
@@ -6019,6 +6021,7 @@ var init_sandbox_file_watcher = __esm({
6019
6021
  type,
6020
6022
  relativePath,
6021
6023
  absolutePath,
6024
+ basePath: this.watchPath,
6022
6025
  sessionId: this.sessionId,
6023
6026
  fileSize,
6024
6027
  timestamp: /* @__PURE__ */ new Date()
@@ -6227,6 +6230,7 @@ var init_sandbox_file_watcher = __esm({
6227
6230
  type: "add",
6228
6231
  relativePath: filePath,
6229
6232
  absolutePath: `${this.basePath}/${filePath}`,
6233
+ basePath: this.basePath,
6230
6234
  sessionId: this.sessionId,
6231
6235
  fileSize: info.size,
6232
6236
  timestamp: /* @__PURE__ */ new Date()
@@ -6239,6 +6243,7 @@ var init_sandbox_file_watcher = __esm({
6239
6243
  type: "unlink",
6240
6244
  relativePath: filePath,
6241
6245
  absolutePath: `${this.basePath}/${filePath}`,
6246
+ basePath: this.basePath,
6242
6247
  sessionId: this.sessionId,
6243
6248
  timestamp: /* @__PURE__ */ new Date()
6244
6249
  });
@@ -6428,24 +6433,72 @@ var init_sandbox_file_sync = __esm({
6428
6433
  removeWebhook() {
6429
6434
  this.webhookConfig = void 0;
6430
6435
  }
6436
+ /**
6437
+ * Extract hostname from URL for display (security - don't expose full URL)
6438
+ */
6439
+ extractHostname(url) {
6440
+ try {
6441
+ const urlObj = new URL(url);
6442
+ return urlObj.hostname;
6443
+ } catch {
6444
+ return "unknown";
6445
+ }
6446
+ }
6447
+ /**
6448
+ * Persist webhook delivery event to the session timeline
6449
+ */
6450
+ async persistWebhookDelivery(sessionId, webhookType, result, filePath, operation) {
6451
+ if (!this.eventStorage || !this.webhookConfig) return;
6452
+ try {
6453
+ const now = /* @__PURE__ */ new Date();
6454
+ const sequenceNumber = await this.getNextSequenceNumber(sessionId);
6455
+ const eventData = {
6456
+ webhookType,
6457
+ targetUrl: this.extractHostname(this.webhookConfig.url),
6458
+ success: result.success,
6459
+ statusCode: result.statusCode,
6460
+ error: result.error,
6461
+ durationMs: result.durationMs,
6462
+ retryCount: result.retryCount > 0 ? result.retryCount : void 0,
6463
+ filePath,
6464
+ operation
6465
+ };
6466
+ const eventType = result.success ? "webhook_delivery" : "webhook_failure";
6467
+ const sessionEvent = {
6468
+ eventType,
6469
+ category: exports.EventCategory.WEBHOOK,
6470
+ startedAt: new Date(now.getTime() - result.durationMs),
6471
+ endedAt: now,
6472
+ durationMs: result.durationMs,
6473
+ eventData,
6474
+ sequenceNumber
6475
+ };
6476
+ await this.eventStorage.saveEvents(sessionId, [sessionEvent]);
6477
+ } catch (error) {
6478
+ console.error("[FILE_SYNC] Error saving webhook delivery event to storage:", error);
6479
+ }
6480
+ }
6431
6481
  /**
6432
6482
  * Send a webhook notification
6433
6483
  * @param payload - The webhook payload to send
6484
+ * @param sessionId - Session ID for persisting delivery events
6485
+ * @returns WebhookDeliveryResult with success/failure info
6434
6486
  */
6435
- async sendWebhook(payload) {
6436
- if (!this.webhookConfig) return;
6487
+ async sendWebhook(payload, sessionId) {
6488
+ if (!this.webhookConfig) return null;
6437
6489
  const config = this.webhookConfig;
6438
6490
  const eventType = payload.fileSyncEvent?.operation ?? "file_change";
6439
6491
  if (config.events && config.events.length > 0) {
6440
6492
  const shouldSend = config.events.some(
6441
6493
  (e) => e === eventType || e === "file_change" && payload.event === "file_change"
6442
6494
  );
6443
- if (!shouldSend) return;
6495
+ if (!shouldSend) return null;
6444
6496
  }
6445
6497
  const body = JSON.stringify(payload);
6446
6498
  const timeoutMs = config.timeoutMs ?? 1e4;
6447
6499
  const retries = config.retries ?? 3;
6448
6500
  const isAsync = config.async !== false;
6501
+ const startTime = Date.now();
6449
6502
  const headers = {
6450
6503
  "Content-Type": "application/json",
6451
6504
  "User-Agent": "Ash-FileSync/1.0",
@@ -6467,12 +6520,23 @@ var init_sandbox_file_sync = __esm({
6467
6520
  signal: controller.signal
6468
6521
  });
6469
6522
  clearTimeout(timeoutId);
6523
+ const durationMs = Date.now() - startTime;
6470
6524
  if (!response.ok) {
6471
- throw new Error(`Webhook returned ${response.status}: ${response.statusText}`);
6525
+ const error = `Webhook returned ${response.status}: ${response.statusText}`;
6526
+ if (attempt < retries) {
6527
+ const delay = Math.pow(2, attempt) * 1e3;
6528
+ console.warn(`[FILE_SYNC] Webhook failed (attempt ${attempt + 1}/${retries}), retrying in ${delay}ms: ${error}`);
6529
+ await new Promise((resolve3) => setTimeout(resolve3, delay));
6530
+ return sendWithRetry(attempt + 1);
6531
+ }
6532
+ console.error(`[FILE_SYNC] Webhook failed after ${retries} attempts: ${error}`);
6533
+ return { success: false, statusCode: response.status, error, durationMs, retryCount: attempt };
6472
6534
  }
6473
6535
  console.log(`[FILE_SYNC] Webhook sent successfully to ${config.url}`);
6536
+ return { success: true, statusCode: response.status, durationMs, retryCount: attempt };
6474
6537
  } catch (error) {
6475
6538
  const errorMsg = error instanceof Error ? error.message : String(error);
6539
+ const durationMs = Date.now() - startTime;
6476
6540
  if (attempt < retries) {
6477
6541
  const delay = Math.pow(2, attempt) * 1e3;
6478
6542
  console.warn(`[FILE_SYNC] Webhook failed (attempt ${attempt + 1}/${retries}), retrying in ${delay}ms: ${errorMsg}`);
@@ -6480,15 +6544,27 @@ var init_sandbox_file_sync = __esm({
6480
6544
  return sendWithRetry(attempt + 1);
6481
6545
  }
6482
6546
  console.error(`[FILE_SYNC] Webhook failed after ${retries} attempts: ${errorMsg}`);
6483
- throw error;
6547
+ return { success: false, error: errorMsg, durationMs, retryCount: attempt };
6484
6548
  }
6485
6549
  };
6550
+ const webhookType = payload.event === "file_sync" ? "file_sync" : "file_change";
6551
+ const filePath = payload.fileSyncEvent?.canonicalPath ?? payload.fileChangeEvent?.relativePath;
6552
+ const operation = payload.fileSyncEvent?.operation ?? payload.fileChangeEvent?.type;
6486
6553
  if (isAsync) {
6487
- sendWithRetry(0).catch((error) => {
6554
+ sendWithRetry(0).then(async (result) => {
6555
+ if (sessionId) {
6556
+ await this.persistWebhookDelivery(sessionId, webhookType, result, filePath, operation);
6557
+ }
6558
+ }).catch((error) => {
6488
6559
  console.error("[FILE_SYNC] Async webhook failed:", error);
6489
6560
  });
6561
+ return null;
6490
6562
  } else {
6491
- await sendWithRetry(0);
6563
+ const result = await sendWithRetry(0);
6564
+ if (sessionId) {
6565
+ await this.persistWebhookDelivery(sessionId, webhookType, result, filePath, operation);
6566
+ }
6567
+ return result;
6492
6568
  }
6493
6569
  }
6494
6570
  /**
@@ -6502,7 +6578,7 @@ var init_sandbox_file_sync = __esm({
6502
6578
  metadata: this.webhookConfig?.metadata,
6503
6579
  fileSyncEvent: event
6504
6580
  };
6505
- await this.sendWebhook(payload);
6581
+ await this.sendWebhook(payload, sessionId);
6506
6582
  }
6507
6583
  /**
6508
6584
  * Send a file change event webhook (from watcher)
@@ -6515,7 +6591,7 @@ var init_sandbox_file_sync = __esm({
6515
6591
  metadata: this.webhookConfig?.metadata,
6516
6592
  fileChangeEvent: event
6517
6593
  };
6518
- await this.sendWebhook(payload);
6594
+ await this.sendWebhook(payload, event.sessionId);
6519
6595
  }
6520
6596
  /**
6521
6597
  * Get the next sequence number for a session
@@ -6601,10 +6677,10 @@ var init_sandbox_file_sync = __esm({
6601
6677
  const isTextFile = diff !== void 0 || event.newContent && !event.newContent.slice(0, 8192).includes(0);
6602
6678
  const eventData = {
6603
6679
  operation: event.operation,
6604
- direction: event.direction,
6605
- source: "internal",
6606
- // File sync operations are internal
6607
- filePath: event.filePath,
6680
+ source: event.source,
6681
+ canonicalPath: event.canonicalPath,
6682
+ basePath: event.basePath,
6683
+ sandboxPath: event.sandboxPath,
6608
6684
  fileSize: event.fileSize,
6609
6685
  success: event.success,
6610
6686
  error: event.error,
@@ -6612,7 +6688,7 @@ var init_sandbox_file_sync = __esm({
6612
6688
  isTextFile,
6613
6689
  previousSize: event.previousContent?.length
6614
6690
  };
6615
- const eventType = `file_${event.operation}`;
6691
+ const eventType = event.operation;
6616
6692
  const sessionEvent = {
6617
6693
  eventType,
6618
6694
  category: exports.EventCategory.FILE,
@@ -6635,6 +6711,13 @@ var init_sandbox_file_sync = __esm({
6635
6711
  setSandboxOperations(ops) {
6636
6712
  this.sandboxOps = ops;
6637
6713
  }
6714
+ /**
6715
+ * Get the effective base path for sandbox operations
6716
+ * @param targetPath - Optional override for the base path
6717
+ */
6718
+ getBasePath(targetPath) {
6719
+ return targetPath ?? this.sandboxBasePath;
6720
+ }
6638
6721
  /**
6639
6722
  * Get the full sandbox path for a file
6640
6723
  * @param path - The relative file path
@@ -6642,18 +6725,36 @@ var init_sandbox_file_sync = __esm({
6642
6725
  */
6643
6726
  getSandboxPath(path15, targetPath) {
6644
6727
  const normalizedPath = path15.replace(/^\/+/, "");
6645
- const basePath = targetPath ?? this.sandboxBasePath;
6728
+ const basePath = this.getBasePath(targetPath);
6646
6729
  if (basePath === ".") {
6647
6730
  return normalizedPath;
6648
6731
  }
6649
6732
  return `${basePath}/${normalizedPath}`;
6650
6733
  }
6734
+ /**
6735
+ * Build all path fields for a FileSyncEvent
6736
+ * @param path - The canonical file path (used as S3 key)
6737
+ * @param targetPath - Optional override for the base path
6738
+ */
6739
+ buildPathFields(path15, targetPath) {
6740
+ const normalizedPath = path15.replace(/^\/+/, "");
6741
+ const basePath = this.getBasePath(targetPath);
6742
+ const sandboxPath = this.getSandboxPath(path15, targetPath);
6743
+ return {
6744
+ canonicalPath: normalizedPath,
6745
+ // The logical path (S3 key)
6746
+ basePath,
6747
+ // The prefix used in sandbox
6748
+ sandboxPath
6749
+ // Full computed path in sandbox
6750
+ };
6751
+ }
6651
6752
  /**
6652
6753
  * Push a file: writes to S3, then to sandbox if running
6653
6754
  * @param sessionId - Session ID
6654
6755
  * @param path - File path (stored in S3 and used as relative path in sandbox)
6655
6756
  * @param content - File content
6656
- * @param options - Push options (e.g., targetPath to override sandbox location)
6757
+ * @param options - Push options (e.g., targetPath, source)
6657
6758
  * @param previousContent - Optional previous content for diff computation
6658
6759
  */
6659
6760
  async pushFile(sessionId, path15, content, options, previousContent) {
@@ -6662,13 +6763,16 @@ var init_sandbox_file_sync = __esm({
6662
6763
  s3Written: false,
6663
6764
  sandboxWritten: false
6664
6765
  };
6766
+ const source = options?.source ?? "client_api";
6767
+ const uploadToStorageOp = source === "client_api" ? "client_uploaded_to_ash_storage" : "agent_sandbox_saved_to_ash_storage";
6768
+ const pathFields = this.buildPathFields(path15, options?.targetPath);
6665
6769
  try {
6666
6770
  await this.fileStore.writeFile(sessionId, path15, content);
6667
6771
  result.s3Written = true;
6668
6772
  await this.emitFileEvent(sessionId, {
6669
- operation: "push",
6670
- direction: "to_s3",
6671
- filePath: path15,
6773
+ operation: uploadToStorageOp,
6774
+ source,
6775
+ ...pathFields,
6672
6776
  fileSize: content.length,
6673
6777
  success: true,
6674
6778
  previousContent,
@@ -6679,9 +6783,9 @@ var init_sandbox_file_sync = __esm({
6679
6783
  result.error = `S3 write failed: ${errorMessage}`;
6680
6784
  console.error(`[FILE_SYNC] S3 write failed for session ${sessionId}, path ${path15}:`, error);
6681
6785
  await this.emitFileEvent(sessionId, {
6682
- operation: "push",
6683
- direction: "to_s3",
6684
- filePath: path15,
6786
+ operation: uploadToStorageOp,
6787
+ source,
6788
+ ...pathFields,
6685
6789
  fileSize: content.length,
6686
6790
  success: false,
6687
6791
  error: errorMessage
@@ -6690,13 +6794,12 @@ var init_sandbox_file_sync = __esm({
6690
6794
  }
6691
6795
  if (this.sandboxOps?.isSandboxRunning(sessionId)) {
6692
6796
  try {
6693
- const sandboxPath = this.getSandboxPath(path15, options?.targetPath);
6694
- const writeResult = await this.sandboxOps.writeFile(sessionId, sandboxPath, content);
6797
+ const writeResult = await this.sandboxOps.writeFile(sessionId, pathFields.sandboxPath, content);
6695
6798
  result.sandboxWritten = writeResult.success;
6696
6799
  await this.emitFileEvent(sessionId, {
6697
- operation: "push",
6698
- direction: "to_sandbox",
6699
- filePath: path15,
6800
+ operation: "ash_storage_synced_to_agent_sandbox",
6801
+ source,
6802
+ ...pathFields,
6700
6803
  fileSize: content.length,
6701
6804
  success: writeResult.success,
6702
6805
  error: writeResult.error
@@ -6708,9 +6811,9 @@ var init_sandbox_file_sync = __esm({
6708
6811
  const errorMessage = extractErrorMessage(error);
6709
6812
  console.warn(`[FILE_SYNC] Sandbox write error for ${path15}: ${errorMessage}`);
6710
6813
  await this.emitFileEvent(sessionId, {
6711
- operation: "push",
6712
- direction: "to_sandbox",
6713
- filePath: path15,
6814
+ operation: "ash_storage_synced_to_agent_sandbox",
6815
+ source,
6816
+ ...pathFields,
6714
6817
  fileSize: content.length,
6715
6818
  success: false,
6716
6819
  error: errorMessage
@@ -6738,8 +6841,12 @@ var init_sandbox_file_sync = __esm({
6738
6841
  /**
6739
6842
  * Pull a file from sandbox to S3
6740
6843
  * Reads from sandbox and writes to S3
6844
+ *
6845
+ * @param sessionId - Session ID
6846
+ * @param path - File path (relative to sandbox)
6847
+ * @param options - Pull options (e.g., targetPath to override sandbox location)
6741
6848
  */
6742
- async pullFile(sessionId, path15) {
6849
+ async pullFile(sessionId, path15, options) {
6743
6850
  const result = {
6744
6851
  path: path15,
6745
6852
  content: null,
@@ -6749,15 +6856,15 @@ var init_sandbox_file_sync = __esm({
6749
6856
  result.error = "Sandbox is not running";
6750
6857
  return result;
6751
6858
  }
6859
+ const pathFields = this.buildPathFields(path15, options?.targetPath);
6752
6860
  try {
6753
- const sandboxPath = this.getSandboxPath(path15);
6754
- const readResult = await this.sandboxOps.readFile(sessionId, sandboxPath);
6861
+ const readResult = await this.sandboxOps.readFile(sessionId, pathFields.sandboxPath);
6755
6862
  if (!readResult.success || !readResult.content) {
6756
6863
  result.error = readResult.error ?? "File not found in sandbox";
6757
6864
  await this.emitFileEvent(sessionId, {
6758
- operation: "pull",
6759
- direction: "from_sandbox",
6760
- filePath: path15,
6865
+ operation: "read_from_agent_sandbox",
6866
+ source: "ash_file_sync",
6867
+ ...pathFields,
6761
6868
  success: false,
6762
6869
  error: result.error
6763
6870
  });
@@ -6765,9 +6872,9 @@ var init_sandbox_file_sync = __esm({
6765
6872
  }
6766
6873
  result.content = readResult.content;
6767
6874
  await this.emitFileEvent(sessionId, {
6768
- operation: "pull",
6769
- direction: "from_sandbox",
6770
- filePath: path15,
6875
+ operation: "read_from_agent_sandbox",
6876
+ source: "ash_file_sync",
6877
+ ...pathFields,
6771
6878
  fileSize: readResult.content.length,
6772
6879
  success: true,
6773
6880
  newContent: readResult.content
@@ -6776,9 +6883,9 @@ var init_sandbox_file_sync = __esm({
6776
6883
  const errorMessage = extractErrorMessage(error);
6777
6884
  result.error = `Sandbox read failed: ${errorMessage}`;
6778
6885
  await this.emitFileEvent(sessionId, {
6779
- operation: "pull",
6780
- direction: "from_sandbox",
6781
- filePath: path15,
6886
+ operation: "read_from_agent_sandbox",
6887
+ source: "ash_file_sync",
6888
+ ...pathFields,
6782
6889
  success: false,
6783
6890
  error: errorMessage
6784
6891
  });
@@ -6788,9 +6895,9 @@ var init_sandbox_file_sync = __esm({
6788
6895
  await this.fileStore.writeFile(sessionId, path15, result.content);
6789
6896
  result.s3Written = true;
6790
6897
  await this.emitFileEvent(sessionId, {
6791
- operation: "pull",
6792
- direction: "to_s3",
6793
- filePath: path15,
6898
+ operation: "agent_sandbox_saved_to_ash_storage",
6899
+ source: "ash_file_sync",
6900
+ ...pathFields,
6794
6901
  fileSize: result.content.length,
6795
6902
  success: true,
6796
6903
  newContent: result.content
@@ -6800,9 +6907,9 @@ var init_sandbox_file_sync = __esm({
6800
6907
  result.error = `S3 write failed: ${errorMessage}`;
6801
6908
  console.error(`[FILE_SYNC] S3 write failed in pullFile for session ${sessionId}, path ${path15}:`, error);
6802
6909
  await this.emitFileEvent(sessionId, {
6803
- operation: "pull",
6804
- direction: "to_s3",
6805
- filePath: path15,
6910
+ operation: "agent_sandbox_saved_to_ash_storage",
6911
+ source: "ash_file_sync",
6912
+ ...pathFields,
6806
6913
  fileSize: result.content?.length,
6807
6914
  success: false,
6808
6915
  error: errorMessage
@@ -6844,43 +6951,43 @@ var init_sandbox_file_sync = __esm({
6844
6951
  */
6845
6952
  async deleteFile(sessionId, path15) {
6846
6953
  const result = { s3Deleted: false, sandboxDeleted: false };
6954
+ const pathFields = this.buildPathFields(path15);
6847
6955
  try {
6848
6956
  await this.fileStore.deleteFile(sessionId, path15);
6849
6957
  result.s3Deleted = true;
6850
6958
  await this.emitFileEvent(sessionId, {
6851
- operation: "delete",
6852
- direction: "from_s3",
6853
- filePath: path15,
6959
+ operation: "deleted_from_ash_storage",
6960
+ source: "ash_file_sync",
6961
+ ...pathFields,
6854
6962
  success: true
6855
6963
  });
6856
6964
  } catch (error) {
6857
6965
  const errorMessage = extractErrorMessage(error);
6858
6966
  console.warn(`[FILE_SYNC] S3 delete failed for ${path15}: ${errorMessage}`);
6859
6967
  await this.emitFileEvent(sessionId, {
6860
- operation: "delete",
6861
- direction: "from_s3",
6862
- filePath: path15,
6968
+ operation: "deleted_from_ash_storage",
6969
+ source: "ash_file_sync",
6970
+ ...pathFields,
6863
6971
  success: false,
6864
6972
  error: errorMessage
6865
6973
  });
6866
6974
  }
6867
6975
  if (this.sandboxOps?.isSandboxRunning(sessionId)) {
6868
6976
  try {
6869
- const sandboxPath = this.getSandboxPath(path15);
6870
- console.log(`[FILE_SYNC] Would delete ${sandboxPath} from sandbox`);
6977
+ console.log(`[FILE_SYNC] Would delete ${pathFields.sandboxPath} from sandbox`);
6871
6978
  result.sandboxDeleted = true;
6872
6979
  await this.emitFileEvent(sessionId, {
6873
- operation: "delete",
6874
- direction: "from_sandbox",
6875
- filePath: path15,
6980
+ operation: "deleted_from_agent_sandbox",
6981
+ source: "ash_file_sync",
6982
+ ...pathFields,
6876
6983
  success: true
6877
6984
  });
6878
6985
  } catch (error) {
6879
6986
  const errorMessage = extractErrorMessage(error);
6880
6987
  await this.emitFileEvent(sessionId, {
6881
- operation: "delete",
6882
- direction: "from_sandbox",
6883
- filePath: path15,
6988
+ operation: "deleted_from_agent_sandbox",
6989
+ source: "ash_file_sync",
6990
+ ...pathFields,
6884
6991
  success: false,
6885
6992
  error: errorMessage
6886
6993
  });
@@ -6900,36 +7007,36 @@ var init_sandbox_file_sync = __esm({
6900
7007
  }
6901
7008
  const files = await this.fileStore.listFiles(sessionId);
6902
7009
  for (const file of files) {
7010
+ const pathFields = this.buildPathFields(file.path);
6903
7011
  try {
6904
7012
  const content = await this.fileStore.readFile(sessionId, file.path);
6905
7013
  if (!content) {
6906
7014
  result.errors.push({ path: file.path, error: "File not found in S3" });
6907
7015
  await this.emitFileEvent(sessionId, {
6908
- operation: "sync_to_sandbox",
6909
- direction: "from_s3",
6910
- filePath: file.path,
7016
+ operation: "ash_storage_synced_to_agent_sandbox",
7017
+ source: "ash_file_sync",
7018
+ ...pathFields,
6911
7019
  success: false,
6912
- error: "File not found in S3"
7020
+ error: "File not found in Ash storage"
6913
7021
  });
6914
7022
  continue;
6915
7023
  }
6916
- const sandboxPath = this.getSandboxPath(file.path);
6917
- const writeResult = await this.sandboxOps.writeFile(sessionId, sandboxPath, content);
7024
+ const writeResult = await this.sandboxOps.writeFile(sessionId, pathFields.sandboxPath, content);
6918
7025
  if (writeResult.success) {
6919
7026
  result.fileCount++;
6920
7027
  await this.emitFileEvent(sessionId, {
6921
- operation: "sync_to_sandbox",
6922
- direction: "to_sandbox",
6923
- filePath: file.path,
7028
+ operation: "ash_storage_synced_to_agent_sandbox",
7029
+ source: "ash_file_sync",
7030
+ ...pathFields,
6924
7031
  fileSize: content.length,
6925
7032
  success: true
6926
7033
  });
6927
7034
  } else {
6928
7035
  result.errors.push({ path: file.path, error: writeResult.error ?? "Unknown error" });
6929
7036
  await this.emitFileEvent(sessionId, {
6930
- operation: "sync_to_sandbox",
6931
- direction: "to_sandbox",
6932
- filePath: file.path,
7037
+ operation: "ash_storage_synced_to_agent_sandbox",
7038
+ source: "ash_file_sync",
7039
+ ...pathFields,
6933
7040
  fileSize: content.length,
6934
7041
  success: false,
6935
7042
  error: writeResult.error
@@ -6942,9 +7049,9 @@ var init_sandbox_file_sync = __esm({
6942
7049
  error: errorMessage
6943
7050
  });
6944
7051
  await this.emitFileEvent(sessionId, {
6945
- operation: "sync_to_sandbox",
6946
- direction: "to_sandbox",
6947
- filePath: file.path,
7052
+ operation: "ash_storage_synced_to_agent_sandbox",
7053
+ source: "ash_file_sync",
7054
+ ...pathFields,
6948
7055
  success: false,
6949
7056
  error: errorMessage
6950
7057
  });
@@ -6975,39 +7082,25 @@ var init_sandbox_file_sync = __esm({
6975
7082
  return result;
6976
7083
  }
6977
7084
  }
6978
- for (const filePath of filesToSync) {
7085
+ for (const file of filesToSync) {
7086
+ const pathFields = this.buildPathFields(file);
6979
7087
  try {
6980
- const pullResult = await this.pullFile(sessionId, filePath);
7088
+ const pullResult = await this.pullFile(sessionId, file);
6981
7089
  if (pullResult.s3Written) {
6982
7090
  result.fileCount++;
6983
- await this.emitFileEvent(sessionId, {
6984
- operation: "sync_from_sandbox",
6985
- direction: "to_s3",
6986
- filePath,
6987
- fileSize: pullResult.content?.length,
6988
- success: true,
6989
- newContent: pullResult.content ?? void 0
6990
- });
6991
7091
  } else if (pullResult.error) {
6992
- result.errors.push({ path: filePath, error: pullResult.error });
6993
- await this.emitFileEvent(sessionId, {
6994
- operation: "sync_from_sandbox",
6995
- direction: "to_s3",
6996
- filePath,
6997
- success: false,
6998
- error: pullResult.error
6999
- });
7092
+ result.errors.push({ path: file, error: pullResult.error });
7000
7093
  }
7001
7094
  } catch (error) {
7002
7095
  const errorMessage = extractErrorMessage(error);
7003
7096
  result.errors.push({
7004
- path: filePath,
7097
+ path: file,
7005
7098
  error: errorMessage
7006
7099
  });
7007
7100
  await this.emitFileEvent(sessionId, {
7008
- operation: "sync_from_sandbox",
7009
- direction: "to_s3",
7010
- filePath,
7101
+ operation: "agent_sandbox_saved_to_ash_storage",
7102
+ source: "ash_file_sync",
7103
+ ...pathFields,
7011
7104
  success: false,
7012
7105
  error: errorMessage
7013
7106
  });
@@ -7042,9 +7135,10 @@ var init_sandbox_file_sync = __esm({
7042
7135
  */
7043
7136
  async startWatching(sessionId, options) {
7044
7137
  const opts = { ...this.defaultWatchOptions, ...options };
7138
+ const watchPath = opts.watchPaths?.[0] ?? ".";
7045
7139
  await this.stopWatching(sessionId);
7046
7140
  const handleFileChange = async (event) => {
7047
- console.log(`[FILE_SYNC] File change detected: ${event.type} ${event.relativePath}`);
7141
+ console.log(`[FILE_SYNC] File change detected: ${event.type} ${event.relativePath} (watching from: ${watchPath})`);
7048
7142
  if (this.webhookConfig) {
7049
7143
  this.sendFileChangeWebhook(event);
7050
7144
  }
@@ -7057,7 +7151,7 @@ var init_sandbox_file_sync = __esm({
7057
7151
  }
7058
7152
  if (event.type === "add" || event.type === "change") {
7059
7153
  try {
7060
- const pullResult = await this.pullFile(sessionId, event.relativePath);
7154
+ const pullResult = await this.pullFile(sessionId, event.relativePath, { targetPath: watchPath });
7061
7155
  if (pullResult.s3Written) {
7062
7156
  console.log(`[FILE_SYNC] Auto-synced ${event.relativePath} to S3`);
7063
7157
  } else if (pullResult.error) {
@@ -7088,7 +7182,7 @@ var init_sandbox_file_sync = __esm({
7088
7182
  });
7089
7183
  await watcher.start();
7090
7184
  this.localWatchers.set(sessionId, watcher);
7091
- console.log(`[FILE_SYNC] Started local file watching for session ${sessionId}`);
7185
+ console.log(`[FILE_SYNC] Started local file watching for session ${sessionId} at ${opts.localPath}`);
7092
7186
  } else {
7093
7187
  if (!this.sandboxOps) {
7094
7188
  throw new Error("Sandbox operations not configured. Call setSandboxOperations first.");
@@ -7096,7 +7190,8 @@ var init_sandbox_file_sync = __esm({
7096
7190
  const watcher = new exports.RemoteSandboxFileWatcher({
7097
7191
  sessionId,
7098
7192
  sandboxOps: this.sandboxOps,
7099
- basePath: this.sandboxBasePath,
7193
+ basePath: watchPath,
7194
+ // Use watchPath instead of sandboxBasePath
7100
7195
  pollIntervalMs: opts.pollIntervalMs ?? 2e3,
7101
7196
  ignored: opts.ignored ?? ["**/node_modules/**", "**/.git/**"],
7102
7197
  onFileChange: handleFileChange,
@@ -7106,7 +7201,7 @@ var init_sandbox_file_sync = __esm({
7106
7201
  });
7107
7202
  await watcher.start();
7108
7203
  this.remoteWatchers.set(sessionId, watcher);
7109
- console.log(`[FILE_SYNC] Started remote file watching for session ${sessionId}`);
7204
+ console.log(`[FILE_SYNC] Started remote file watching for session ${sessionId} at path: ${watchPath}`);
7110
7205
  }
7111
7206
  }
7112
7207
  /**
@@ -7419,6 +7514,7 @@ WATCHER_EOF`;
7419
7514
  type: data.type,
7420
7515
  relativePath: data.path.replace(/^\.\//, ""),
7421
7516
  absolutePath: data.path,
7517
+ basePath: this.watchPath,
7422
7518
  sessionId: this.sessionId,
7423
7519
  timestamp: new Date(data.timestamp)
7424
7520
  });
@@ -16241,8 +16337,10 @@ var init_schema = __esm({
16241
16337
  // error events
16242
16338
  "file",
16243
16339
  // file_push, file_pull, file_sync (file sync operations)
16244
- "input"
16340
+ "input",
16245
16341
  // user_input (user prompts/messages)
16342
+ "webhook"
16343
+ // webhook_delivery, webhook_failure (outbound webhook events)
16246
16344
  ]);
16247
16345
  sessions = pgCore.pgTable(
16248
16346
  "sessions",