@yushaw/sanqian-sdk 0.3.26 → 0.3.28

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/README.md CHANGED
@@ -341,6 +341,25 @@ const response = await sdk.chat('agent', messages, {
341
341
  })
342
342
  ```
343
343
 
344
+ ### Local file ingest
345
+
346
+ For same-machine deployments where the Sanqian backend can read a local path directly, use `uploadFile()` first, then pass the returned `file.path` into `chat()`:
347
+
348
+ ```typescript
349
+ const uploaded = await sdk.uploadFile('/absolute/path/report.pdf', {
350
+ conversationId,
351
+ autoIndex: true,
352
+ asyncProcess: true,
353
+ })
354
+
355
+ const response = await sdk.chat('agent', messages, {
356
+ conversationId: uploaded.conversationId,
357
+ filePaths: [uploaded.file.path],
358
+ })
359
+ ```
360
+
361
+ `uploadFile(localPath)` is not a remote file transfer protocol. For remote/backend-on-another-machine cases, continue using explicit byte upload APIs.
362
+
344
363
  ---
345
364
 
346
365
  ## Human-in-the-Loop (HITL)
@@ -1328,6 +1347,25 @@ const response = await sdk.chat('agent', messages, {
1328
1347
  })
1329
1348
  ```
1330
1349
 
1350
+ ### 本地文件导入
1351
+
1352
+ 对于 Sanqian 后端与调用方在同一台机器、且后端能直接读取本地路径的场景,先调用 `uploadFile()`,再把返回的 `file.path` 传给 `chat()`:
1353
+
1354
+ ```typescript
1355
+ const uploaded = await sdk.uploadFile('/absolute/path/report.pdf', {
1356
+ conversationId,
1357
+ autoIndex: true,
1358
+ asyncProcess: true,
1359
+ })
1360
+
1361
+ const response = await sdk.chat('agent', messages, {
1362
+ conversationId: uploaded.conversationId,
1363
+ filePaths: [uploaded.file.path],
1364
+ })
1365
+ ```
1366
+
1367
+ `uploadFile(localPath)` 不是远程文件传输协议。对于远程 backend 或不同机器场景,仍应使用显式字节上传能力。
1368
+
1331
1369
  ---
1332
1370
 
1333
1371
  ## 人机协作 (HITL)
@@ -297,6 +297,30 @@ interface ChatStreamEvent {
297
297
  result?: unknown;
298
298
  /** Whether tool execution succeeded (for "tool_result" event) */
299
299
  success?: boolean;
300
+ /** Normalized tool execution status (for "tool_result" event) */
301
+ status?: "running" | "completed" | "error" | "cancelled";
302
+ /** Structured action required hint (for "tool_result" event) */
303
+ action_required?: string;
304
+ /** Optional settings deep link target (for "tool_result" event) */
305
+ settings_tab?: string;
306
+ /** Optional settings deep link sub-target (for "tool_result" event) */
307
+ settings_sub_tab?: string;
308
+ /** Structured SDK tool failure marker (for "tool_result" event) */
309
+ sdk_tool_error?: boolean;
310
+ /** App name that emitted the SDK tool failure (for "tool_result" event) */
311
+ sdk_tool_app?: string;
312
+ /** Failure domain for structured tool errors (for "tool_result" event) */
313
+ tool_failure_domain?: string;
314
+ /** App identifier for structured tool errors (for "tool_result" event) */
315
+ tool_failure_app?: string;
316
+ /** Failure kind for structured tool errors (for "tool_result" event) */
317
+ tool_failure_kind?: string;
318
+ /** Whether the tool reported a rollback during failure handling (for "tool_result" event) */
319
+ tool_failure_rolled_back?: boolean;
320
+ /** Human-readable failure message for structured tool errors (for "tool_result" event) */
321
+ tool_failure_error?: string;
322
+ /** Stable target key for structured tool errors (for "tool_result" event) */
323
+ tool_failure_target_key?: string;
300
324
  /** Conversation ID (for "done" event) */
301
325
  conversationId?: string;
302
326
  /** Conversation title (for "done" event) */
@@ -519,6 +543,27 @@ interface ConversationHistoryResult {
519
543
  user_loaded_tools?: string[];
520
544
  user_loaded_subagents?: string[];
521
545
  }
546
+ interface UploadFileOptions {
547
+ conversationId?: string;
548
+ autoIndex?: boolean;
549
+ asyncProcess?: boolean;
550
+ }
551
+ interface UploadedFileInfo {
552
+ id?: number | null;
553
+ path: string;
554
+ name: string;
555
+ size: number;
556
+ mimeType?: string | null;
557
+ extension?: string | null;
558
+ source?: string | null;
559
+ processStatus?: string | null;
560
+ resourceUri?: string | null;
561
+ resourceStatus?: string | null;
562
+ }
563
+ interface UploadFileResult {
564
+ conversationId: string;
565
+ file: UploadedFileInfo;
566
+ }
522
567
  /** Agent update configuration (all fields optional except agent_id) */
523
568
  interface AgentUpdateConfig {
524
569
  /** Agent ID to update */
@@ -998,7 +1043,9 @@ declare abstract class SanqianSDKBase {
998
1043
  private resolvePendingStartIndex;
999
1044
  private getActiveConnectionInfo;
1000
1045
  private getHttpBaseUrl;
1001
- getHttpAuthHeaders(): Record<string, string>;
1046
+ getHttpAuthHeaders(options?: {
1047
+ includeAppIdentity?: boolean;
1048
+ }): Record<string, string>;
1002
1049
  /**
1003
1050
  * Centralized HTTP request method.
1004
1051
  * Automatically injects X-App-Token auth header and standardizes error handling.
@@ -1156,6 +1203,11 @@ declare abstract class SanqianSDKBase {
1156
1203
  * Delete a conversation
1157
1204
  */
1158
1205
  deleteConversation(conversationId: string): Promise<void>;
1206
+ /**
1207
+ * Prepare a local file for chat attachment via the backend ingest-local route.
1208
+ * This is intended for same-machine deployments where the backend can read localPath directly.
1209
+ */
1210
+ uploadFile(localPath: string, options?: UploadFileOptions): Promise<UploadFileResult>;
1159
1211
  /**
1160
1212
  * Send a chat message to an agent (non-streaming)
1161
1213
  */
@@ -297,6 +297,30 @@ interface ChatStreamEvent {
297
297
  result?: unknown;
298
298
  /** Whether tool execution succeeded (for "tool_result" event) */
299
299
  success?: boolean;
300
+ /** Normalized tool execution status (for "tool_result" event) */
301
+ status?: "running" | "completed" | "error" | "cancelled";
302
+ /** Structured action required hint (for "tool_result" event) */
303
+ action_required?: string;
304
+ /** Optional settings deep link target (for "tool_result" event) */
305
+ settings_tab?: string;
306
+ /** Optional settings deep link sub-target (for "tool_result" event) */
307
+ settings_sub_tab?: string;
308
+ /** Structured SDK tool failure marker (for "tool_result" event) */
309
+ sdk_tool_error?: boolean;
310
+ /** App name that emitted the SDK tool failure (for "tool_result" event) */
311
+ sdk_tool_app?: string;
312
+ /** Failure domain for structured tool errors (for "tool_result" event) */
313
+ tool_failure_domain?: string;
314
+ /** App identifier for structured tool errors (for "tool_result" event) */
315
+ tool_failure_app?: string;
316
+ /** Failure kind for structured tool errors (for "tool_result" event) */
317
+ tool_failure_kind?: string;
318
+ /** Whether the tool reported a rollback during failure handling (for "tool_result" event) */
319
+ tool_failure_rolled_back?: boolean;
320
+ /** Human-readable failure message for structured tool errors (for "tool_result" event) */
321
+ tool_failure_error?: string;
322
+ /** Stable target key for structured tool errors (for "tool_result" event) */
323
+ tool_failure_target_key?: string;
300
324
  /** Conversation ID (for "done" event) */
301
325
  conversationId?: string;
302
326
  /** Conversation title (for "done" event) */
@@ -519,6 +543,27 @@ interface ConversationHistoryResult {
519
543
  user_loaded_tools?: string[];
520
544
  user_loaded_subagents?: string[];
521
545
  }
546
+ interface UploadFileOptions {
547
+ conversationId?: string;
548
+ autoIndex?: boolean;
549
+ asyncProcess?: boolean;
550
+ }
551
+ interface UploadedFileInfo {
552
+ id?: number | null;
553
+ path: string;
554
+ name: string;
555
+ size: number;
556
+ mimeType?: string | null;
557
+ extension?: string | null;
558
+ source?: string | null;
559
+ processStatus?: string | null;
560
+ resourceUri?: string | null;
561
+ resourceStatus?: string | null;
562
+ }
563
+ interface UploadFileResult {
564
+ conversationId: string;
565
+ file: UploadedFileInfo;
566
+ }
522
567
  /** Agent update configuration (all fields optional except agent_id) */
523
568
  interface AgentUpdateConfig {
524
569
  /** Agent ID to update */
@@ -998,7 +1043,9 @@ declare abstract class SanqianSDKBase {
998
1043
  private resolvePendingStartIndex;
999
1044
  private getActiveConnectionInfo;
1000
1045
  private getHttpBaseUrl;
1001
- getHttpAuthHeaders(): Record<string, string>;
1046
+ getHttpAuthHeaders(options?: {
1047
+ includeAppIdentity?: boolean;
1048
+ }): Record<string, string>;
1002
1049
  /**
1003
1050
  * Centralized HTTP request method.
1004
1051
  * Automatically injects X-App-Token auth header and standardizes error handling.
@@ -1156,6 +1203,11 @@ declare abstract class SanqianSDKBase {
1156
1203
  * Delete a conversation
1157
1204
  */
1158
1205
  deleteConversation(conversationId: string): Promise<void>;
1206
+ /**
1207
+ * Prepare a local file for chat attachment via the backend ingest-local route.
1208
+ * This is intended for same-machine deployments where the backend can read localPath directly.
1209
+ */
1210
+ uploadFile(localPath: string, options?: UploadFileOptions): Promise<UploadFileResult>;
1159
1211
  /**
1160
1212
  * Send a chat message to an agent (non-streaming)
1161
1213
  */
@@ -368,10 +368,20 @@ var SanqianSDKBase = class _SanqianSDKBase {
368
368
  const host = info.ws_host || "127.0.0.1";
369
369
  return `${protocol}://${host}:${info.port}`;
370
370
  }
371
- getHttpAuthHeaders() {
371
+ getHttpAuthHeaders(options) {
372
372
  const info = this.getActiveConnectionInfo();
373
373
  const token = info?.token;
374
- return token ? { "X-App-Token": token } : {};
374
+ const headers = token ? { "X-App-Token": token } : {};
375
+ if (options?.includeAppIdentity) {
376
+ headers["X-App-Name"] = this.config.appName;
377
+ if (this.config.appVersion) {
378
+ headers["X-App-Version"] = this.config.appVersion;
379
+ }
380
+ if (this.config.displayName) {
381
+ headers["X-App-Display-Name"] = this.config.displayName;
382
+ }
383
+ }
384
+ return headers;
375
385
  }
376
386
  /**
377
387
  * Centralized HTTP request method.
@@ -384,11 +394,18 @@ var SanqianSDKBase = class _SanqianSDKBase {
384
394
  if (qs) {
385
395
  url += `?${qs}`;
386
396
  }
387
- const headers = { ...this.getHttpAuthHeaders() };
397
+ const headers = {
398
+ ...this.getHttpAuthHeaders({ includeAppIdentity: options?.includeAppIdentity })
399
+ };
388
400
  const init = { method, headers };
389
401
  if (options?.body !== void 0) {
390
402
  headers["Content-Type"] = "application/json";
391
403
  init.body = JSON.stringify(options.body);
404
+ } else if (options?.rawBody !== void 0) {
405
+ if (options.contentType) {
406
+ headers["Content-Type"] = options.contentType;
407
+ }
408
+ init.body = options.rawBody;
392
409
  }
393
410
  const response = await fetch(url, init);
394
411
  if (!response.ok) {
@@ -784,7 +801,19 @@ var SanqianSDKBase = class _SanqianSDKBase {
784
801
  tool_call_id: tool_result.call_id,
785
802
  result: tool_result.result,
786
803
  success: tool_result.success,
787
- error: tool_result.error
804
+ error: tool_result.error,
805
+ status: tool_result.status,
806
+ action_required: tool_result.action_required,
807
+ settings_tab: tool_result.settings_tab,
808
+ settings_sub_tab: tool_result.settings_sub_tab,
809
+ sdk_tool_error: tool_result.sdk_tool_error,
810
+ sdk_tool_app: tool_result.sdk_tool_app,
811
+ tool_failure_domain: tool_result.tool_failure_domain,
812
+ tool_failure_app: tool_result.tool_failure_app,
813
+ tool_failure_kind: tool_result.tool_failure_kind,
814
+ tool_failure_rolled_back: tool_result.tool_failure_rolled_back,
815
+ tool_failure_error: tool_result.tool_failure_error,
816
+ tool_failure_target_key: tool_result.tool_failure_target_key
788
817
  });
789
818
  } else {
790
819
  this.log("Received tool_result event without tool_result data");
@@ -896,10 +925,22 @@ var SanqianSDKBase = class _SanqianSDKBase {
896
925
  if (handler) {
897
926
  handler.onEvent({
898
927
  type: "tool_result",
899
- tool_call_id: message.tool_call_id,
928
+ tool_call_id: message.tool_call_id || message.tool_id,
900
929
  result: message.result,
901
930
  success: message.success,
902
- error: message.error
931
+ error: message.error,
932
+ status: message.status,
933
+ action_required: message.action_required,
934
+ settings_tab: message.settings_tab,
935
+ settings_sub_tab: message.settings_sub_tab,
936
+ sdk_tool_error: message.sdk_tool_error,
937
+ sdk_tool_app: message.sdk_tool_app,
938
+ tool_failure_domain: message.tool_failure_domain,
939
+ tool_failure_app: message.tool_failure_app,
940
+ tool_failure_kind: message.tool_failure_kind,
941
+ tool_failure_rolled_back: message.tool_failure_rolled_back,
942
+ tool_failure_error: message.tool_failure_error,
943
+ tool_failure_target_key: message.tool_failure_target_key
903
944
  });
904
945
  }
905
946
  }
@@ -970,11 +1011,12 @@ var SanqianSDKBase = class _SanqianSDKBase {
970
1011
  } catch (e) {
971
1012
  const errorMessage = e instanceof Error ? e.message : String(e);
972
1013
  const errorStack = e instanceof Error ? e.stack : void 0;
1014
+ const structuredToolResult = e && typeof e === "object" && "toolResult" in e ? e.toolResult : void 0;
973
1015
  this.log(`Tool '${toolName}' failed:`, errorMessage);
974
1016
  if (errorStack) {
975
1017
  this.log(`Tool error stack:`, errorStack);
976
1018
  }
977
- await this.sendToolResult(msgId, call_id, false, void 0, errorMessage);
1019
+ await this.sendToolResult(msgId, call_id, false, structuredToolResult, errorMessage);
978
1020
  }
979
1021
  }
980
1022
  async sendToolResult(id, callId, success, result, error) {
@@ -1518,6 +1560,46 @@ var SanqianSDKBase = class _SanqianSDKBase {
1518
1560
  throw createSDKError("CONVERSATION_NOT_FOUND" /* CONVERSATION_NOT_FOUND */, response.error);
1519
1561
  }
1520
1562
  }
1563
+ /**
1564
+ * Prepare a local file for chat attachment via the backend ingest-local route.
1565
+ * This is intended for same-machine deployments where the backend can read localPath directly.
1566
+ */
1567
+ async uploadFile(localPath, options) {
1568
+ await this.ensureHttpReady();
1569
+ const form = new URLSearchParams();
1570
+ form.append("local_path", localPath);
1571
+ if (options?.conversationId) {
1572
+ form.append("conversation_id", options.conversationId);
1573
+ }
1574
+ form.append("auto_index", String(options?.autoIndex ?? true));
1575
+ form.append("async_process", String(options?.asyncProcess ?? true));
1576
+ const response = await this.httpRequest("POST", "/api/files/ingest-local", {
1577
+ rawBody: form.toString(),
1578
+ contentType: "application/x-www-form-urlencoded",
1579
+ includeAppIdentity: true
1580
+ });
1581
+ if (!response.success || !response.conversation_id || !response.file?.path) {
1582
+ throw createSDKError(
1583
+ "REQUEST_FAILED" /* REQUEST_FAILED */,
1584
+ response.error || "POST /api/files/ingest-local returned an invalid response"
1585
+ );
1586
+ }
1587
+ return {
1588
+ conversationId: response.conversation_id,
1589
+ file: {
1590
+ id: response.file.id,
1591
+ path: response.file.path,
1592
+ name: response.file.name || "",
1593
+ size: response.file.size ?? 0,
1594
+ mimeType: response.file.mime_type,
1595
+ extension: response.file.extension,
1596
+ source: response.file.source,
1597
+ processStatus: response.file.process_status,
1598
+ resourceUri: response.file.resource_uri,
1599
+ resourceStatus: response.file.resource_status
1600
+ }
1601
+ };
1602
+ }
1521
1603
  // ============================================
1522
1604
  // Chat API
1523
1605
  // ============================================