@yushaw/sanqian-chat 0.2.27 → 0.2.30
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/core/index.d.mts +2 -1
- package/dist/core/index.d.ts +2 -1
- package/dist/main/index.d.mts +2 -1
- package/dist/main/index.d.ts +2 -1
- package/dist/main/index.js +139 -34
- package/dist/main/index.mjs +139 -34
- package/dist/preload/factories.d.mts +2 -1
- package/dist/preload/factories.d.ts +2 -1
- package/dist/renderer/index.d.mts +3 -2
- package/dist/renderer/index.d.ts +3 -2
- package/dist/renderer/index.js +125 -128
- package/dist/renderer/index.mjs +125 -128
- package/package.json +1 -1
package/dist/core/index.d.mts
CHANGED
|
@@ -74,7 +74,8 @@ interface ChatUiConfigSerializable {
|
|
|
74
74
|
/** Font size scale: small (13px), normal (14px), large (16px), extra-large (18px) */
|
|
75
75
|
fontSize?: ChatFontSize;
|
|
76
76
|
accentColor?: string;
|
|
77
|
-
|
|
77
|
+
/** Supports Locale plus BCP-47 variants (e.g. zh-CN, en-US) */
|
|
78
|
+
locale?: Locale | string;
|
|
78
79
|
strings?: Partial<ChatUiStrings>;
|
|
79
80
|
alwaysOnTop?: boolean;
|
|
80
81
|
}
|
package/dist/core/index.d.ts
CHANGED
|
@@ -74,7 +74,8 @@ interface ChatUiConfigSerializable {
|
|
|
74
74
|
/** Font size scale: small (13px), normal (14px), large (16px), extra-large (18px) */
|
|
75
75
|
fontSize?: ChatFontSize;
|
|
76
76
|
accentColor?: string;
|
|
77
|
-
|
|
77
|
+
/** Supports Locale plus BCP-47 variants (e.g. zh-CN, en-US) */
|
|
78
|
+
locale?: Locale | string;
|
|
78
79
|
strings?: Partial<ChatUiStrings>;
|
|
79
80
|
alwaysOnTop?: boolean;
|
|
80
81
|
}
|
package/dist/main/index.d.mts
CHANGED
|
@@ -507,7 +507,8 @@ interface ChatUiConfigSerializable {
|
|
|
507
507
|
/** Font size scale: small (13px), normal (14px), large (16px), extra-large (18px) */
|
|
508
508
|
fontSize?: ChatFontSize;
|
|
509
509
|
accentColor?: string;
|
|
510
|
-
|
|
510
|
+
/** Supports Locale plus BCP-47 variants (e.g. zh-CN, en-US) */
|
|
511
|
+
locale?: Locale | string;
|
|
511
512
|
strings?: Partial<ChatUiStrings>;
|
|
512
513
|
alwaysOnTop?: boolean;
|
|
513
514
|
}
|
package/dist/main/index.d.ts
CHANGED
|
@@ -507,7 +507,8 @@ interface ChatUiConfigSerializable {
|
|
|
507
507
|
/** Font size scale: small (13px), normal (14px), large (16px), extra-large (18px) */
|
|
508
508
|
fontSize?: ChatFontSize;
|
|
509
509
|
accentColor?: string;
|
|
510
|
-
|
|
510
|
+
/** Supports Locale plus BCP-47 variants (e.g. zh-CN, en-US) */
|
|
511
|
+
locale?: Locale | string;
|
|
511
512
|
strings?: Partial<ChatUiStrings>;
|
|
512
513
|
alwaysOnTop?: boolean;
|
|
513
514
|
}
|
package/dist/main/index.js
CHANGED
|
@@ -390,6 +390,39 @@ function resolveHitlRunIdFromStreams(params, activeStreams) {
|
|
|
390
390
|
const first = activeRunIds.values().next();
|
|
391
391
|
return first.done ? null : first.value;
|
|
392
392
|
}
|
|
393
|
+
function resolveOwnedHitlRunIdFromStreams(params, activeStreams, senderWebContentsId) {
|
|
394
|
+
if (params.streamId) {
|
|
395
|
+
const stream = activeStreams.get(params.streamId);
|
|
396
|
+
if (!stream || stream.cancelled) {
|
|
397
|
+
return null;
|
|
398
|
+
}
|
|
399
|
+
if (stream.ownerWebContentsId !== senderWebContentsId) {
|
|
400
|
+
return null;
|
|
401
|
+
}
|
|
402
|
+
return stream.runId ?? null;
|
|
403
|
+
}
|
|
404
|
+
if (params.runId) {
|
|
405
|
+
for (const stream of activeStreams.values()) {
|
|
406
|
+
if (stream.cancelled || !stream.runId) continue;
|
|
407
|
+
if (stream.ownerWebContentsId !== senderWebContentsId) continue;
|
|
408
|
+
if (stream.runId === params.runId) {
|
|
409
|
+
return params.runId;
|
|
410
|
+
}
|
|
411
|
+
}
|
|
412
|
+
return null;
|
|
413
|
+
}
|
|
414
|
+
const activeRunIds = /* @__PURE__ */ new Set();
|
|
415
|
+
for (const stream of activeStreams.values()) {
|
|
416
|
+
if (stream.cancelled || !stream.runId) continue;
|
|
417
|
+
if (stream.ownerWebContentsId !== senderWebContentsId) continue;
|
|
418
|
+
activeRunIds.add(stream.runId);
|
|
419
|
+
if (activeRunIds.size > 1) {
|
|
420
|
+
return null;
|
|
421
|
+
}
|
|
422
|
+
}
|
|
423
|
+
const first = activeRunIds.values().next();
|
|
424
|
+
return first.done ? null : first.value;
|
|
425
|
+
}
|
|
393
426
|
|
|
394
427
|
// src/main/FloatingWindow.ts
|
|
395
428
|
var ipcHandlersRegistered = false;
|
|
@@ -425,7 +458,10 @@ var FloatingWindow = class _FloatingWindow {
|
|
|
425
458
|
import_electron.app.whenReady().then(() => this.registerShortcut());
|
|
426
459
|
}
|
|
427
460
|
}
|
|
428
|
-
resolveHitlRunId(params) {
|
|
461
|
+
resolveHitlRunId(params, senderWebContentsId) {
|
|
462
|
+
if (typeof senderWebContentsId === "number") {
|
|
463
|
+
return resolveOwnedHitlRunIdFromStreams(params, this.activeStreams, senderWebContentsId);
|
|
464
|
+
}
|
|
429
465
|
return resolveHitlRunIdFromStreams(params, this.activeStreams);
|
|
430
466
|
}
|
|
431
467
|
/**
|
|
@@ -668,7 +704,13 @@ var FloatingWindow = class _FloatingWindow {
|
|
|
668
704
|
webContents.send("sanqian-chat:streamEvent", { streamId, event: { type: "error", error: "SDK or agent not ready" } });
|
|
669
705
|
return;
|
|
670
706
|
}
|
|
671
|
-
const streamState = {
|
|
707
|
+
const streamState = {
|
|
708
|
+
cancelled: false,
|
|
709
|
+
runId: null,
|
|
710
|
+
cancelSignalSent: false,
|
|
711
|
+
pendingHitlResponse: null,
|
|
712
|
+
ownerWebContentsId: webContents.id
|
|
713
|
+
};
|
|
672
714
|
activeInstance?.activeStreams.set(streamId, streamState);
|
|
673
715
|
try {
|
|
674
716
|
await sdk.ensureReady();
|
|
@@ -683,6 +725,14 @@ var FloatingWindow = class _FloatingWindow {
|
|
|
683
725
|
if (evtWithRunId.run_id && !streamState.runId) {
|
|
684
726
|
streamState.runId = evtWithRunId.run_id;
|
|
685
727
|
}
|
|
728
|
+
if (streamState.pendingHitlResponse && streamState.runId) {
|
|
729
|
+
try {
|
|
730
|
+
sdk.sendHitlResponse(streamState.runId, streamState.pendingHitlResponse);
|
|
731
|
+
streamState.pendingHitlResponse = null;
|
|
732
|
+
} catch (e) {
|
|
733
|
+
console.warn("[FloatingWindow] Failed to flush queued HITL response:", e);
|
|
734
|
+
}
|
|
735
|
+
}
|
|
686
736
|
if (streamState.cancelled && streamState.runId && !streamState.cancelSignalSent) {
|
|
687
737
|
try {
|
|
688
738
|
sdk.cancelRun(streamState.runId);
|
|
@@ -744,31 +794,50 @@ var FloatingWindow = class _FloatingWindow {
|
|
|
744
794
|
activeInstance?.activeStreams.delete(streamId);
|
|
745
795
|
}
|
|
746
796
|
});
|
|
747
|
-
import_electron.ipcMain.handle("sanqian-chat:cancelStream", (
|
|
797
|
+
import_electron.ipcMain.handle("sanqian-chat:cancelStream", (event, params) => {
|
|
748
798
|
const stream = activeInstance?.activeStreams.get(params.streamId);
|
|
749
|
-
if (stream) {
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
799
|
+
if (!stream) {
|
|
800
|
+
return { success: false, error: "stream_not_found" };
|
|
801
|
+
}
|
|
802
|
+
if (stream.ownerWebContentsId !== event.sender.id) {
|
|
803
|
+
console.warn("[FloatingWindow] Rejecting cancelStream from non-owner sender");
|
|
804
|
+
return { success: false, error: "stream_not_owned_by_sender" };
|
|
805
|
+
}
|
|
806
|
+
stream.cancelled = true;
|
|
807
|
+
if (stream.runId && !stream.cancelSignalSent) {
|
|
808
|
+
const sdk = activeInstance ? _FloatingWindow.getSdkFromOptions(activeInstance.options) : null;
|
|
809
|
+
if (sdk) {
|
|
810
|
+
try {
|
|
811
|
+
sdk.cancelRun(stream.runId);
|
|
812
|
+
stream.cancelSignalSent = true;
|
|
813
|
+
} catch (e) {
|
|
814
|
+
console.warn("[FloatingWindow] Failed to cancel run:", e);
|
|
760
815
|
}
|
|
761
816
|
}
|
|
762
817
|
}
|
|
763
818
|
return { success: true };
|
|
764
819
|
});
|
|
765
|
-
import_electron.ipcMain.handle("sanqian-chat:hitlResponse", (
|
|
820
|
+
import_electron.ipcMain.handle("sanqian-chat:hitlResponse", (event, params) => {
|
|
766
821
|
const sdk = activeInstance ? _FloatingWindow.getSdkFromOptions(activeInstance.options) : null;
|
|
767
|
-
const
|
|
822
|
+
const senderWebContentsId = event.sender.id;
|
|
823
|
+
const runId = activeInstance?.resolveHitlRunId(
|
|
824
|
+
{ runId: params.runId, streamId: params.streamId },
|
|
825
|
+
senderWebContentsId
|
|
826
|
+
) ?? null;
|
|
768
827
|
if (sdk && runId) {
|
|
769
828
|
sdk.sendHitlResponse(runId, params.response);
|
|
829
|
+
} else if (params.streamId) {
|
|
830
|
+
const stream = activeInstance?.activeStreams.get(params.streamId);
|
|
831
|
+
if (stream && !stream.cancelled && stream.ownerWebContentsId === senderWebContentsId) {
|
|
832
|
+
stream.pendingHitlResponse = params.response;
|
|
833
|
+
if (activeInstance?.options.devMode) {
|
|
834
|
+
console.warn("[FloatingWindow] Queued HITL response while waiting for runId");
|
|
835
|
+
}
|
|
836
|
+
} else {
|
|
837
|
+
console.warn("[FloatingWindow] HITL response dropped: stream not found/cancelled/not-owned");
|
|
838
|
+
}
|
|
770
839
|
} else if (activeInstance?.options.devMode) {
|
|
771
|
-
console.warn("[FloatingWindow] HITL response dropped: missing runId");
|
|
840
|
+
console.warn("[FloatingWindow] HITL response dropped: missing or unauthorized runId");
|
|
772
841
|
}
|
|
773
842
|
return { success: true };
|
|
774
843
|
});
|
|
@@ -1721,7 +1790,10 @@ var ChatPanel = class {
|
|
|
1721
1790
|
* Resolve HITL runId with deterministic binding for concurrent streams.
|
|
1722
1791
|
* Priority: explicit runId -> streamId-bound runId -> single active stream fallback.
|
|
1723
1792
|
*/
|
|
1724
|
-
resolveHitlRunId(params) {
|
|
1793
|
+
resolveHitlRunId(params, senderWebContentsId) {
|
|
1794
|
+
if (typeof senderWebContentsId === "number") {
|
|
1795
|
+
return resolveOwnedHitlRunIdFromStreams(params, this.activeStreams, senderWebContentsId);
|
|
1796
|
+
}
|
|
1725
1797
|
return resolveHitlRunIdFromStreams(params, this.activeStreams);
|
|
1726
1798
|
}
|
|
1727
1799
|
/**
|
|
@@ -1785,7 +1857,13 @@ var ChatPanel = class {
|
|
|
1785
1857
|
webContents.send("sanqian-chat:streamEvent", { streamId, event: { type: "error", error: "SDK or agent not ready" } });
|
|
1786
1858
|
return;
|
|
1787
1859
|
}
|
|
1788
|
-
const streamState = {
|
|
1860
|
+
const streamState = {
|
|
1861
|
+
cancelled: false,
|
|
1862
|
+
runId: null,
|
|
1863
|
+
cancelSignalSent: false,
|
|
1864
|
+
pendingHitlResponse: null,
|
|
1865
|
+
ownerWebContentsId: webContents.id
|
|
1866
|
+
};
|
|
1789
1867
|
activeInstance2?.activeStreams.set(streamId, streamState);
|
|
1790
1868
|
try {
|
|
1791
1869
|
await sdk.ensureReady();
|
|
@@ -1805,6 +1883,14 @@ var ChatPanel = class {
|
|
|
1805
1883
|
if (evtWithRunId.run_id && !streamState.runId) {
|
|
1806
1884
|
streamState.runId = evtWithRunId.run_id;
|
|
1807
1885
|
}
|
|
1886
|
+
if (streamState.pendingHitlResponse && streamState.runId) {
|
|
1887
|
+
try {
|
|
1888
|
+
sdk.sendHitlResponse(streamState.runId, streamState.pendingHitlResponse);
|
|
1889
|
+
streamState.pendingHitlResponse = null;
|
|
1890
|
+
} catch (e) {
|
|
1891
|
+
console.warn("[ChatPanel] Failed to flush queued HITL response:", e);
|
|
1892
|
+
}
|
|
1893
|
+
}
|
|
1808
1894
|
if (streamState.cancelled && streamState.runId && !streamState.cancelSignalSent) {
|
|
1809
1895
|
try {
|
|
1810
1896
|
sdk.cancelRun(streamState.runId);
|
|
@@ -1866,31 +1952,50 @@ var ChatPanel = class {
|
|
|
1866
1952
|
activeInstance2?.activeStreams.delete(streamId);
|
|
1867
1953
|
}
|
|
1868
1954
|
});
|
|
1869
|
-
import_electron2.ipcMain.handle("sanqian-chat:cancelStream", (
|
|
1955
|
+
import_electron2.ipcMain.handle("sanqian-chat:cancelStream", (event, params) => {
|
|
1870
1956
|
const stream = activeInstance2?.activeStreams.get(params.streamId);
|
|
1871
|
-
if (stream) {
|
|
1872
|
-
|
|
1873
|
-
|
|
1874
|
-
|
|
1875
|
-
|
|
1876
|
-
|
|
1877
|
-
|
|
1878
|
-
|
|
1879
|
-
|
|
1880
|
-
|
|
1881
|
-
|
|
1957
|
+
if (!stream) {
|
|
1958
|
+
return { success: false, error: "stream_not_found" };
|
|
1959
|
+
}
|
|
1960
|
+
if (stream.ownerWebContentsId !== event.sender.id) {
|
|
1961
|
+
console.warn("[ChatPanel] Rejecting cancelStream from non-owner sender");
|
|
1962
|
+
return { success: false, error: "stream_not_owned_by_sender" };
|
|
1963
|
+
}
|
|
1964
|
+
stream.cancelled = true;
|
|
1965
|
+
if (stream.runId && !stream.cancelSignalSent) {
|
|
1966
|
+
const sdk = activeInstance2?.getSdk();
|
|
1967
|
+
if (sdk) {
|
|
1968
|
+
try {
|
|
1969
|
+
sdk.cancelRun(stream.runId);
|
|
1970
|
+
stream.cancelSignalSent = true;
|
|
1971
|
+
} catch (e) {
|
|
1972
|
+
console.warn("[ChatPanel] Failed to cancel run:", e);
|
|
1882
1973
|
}
|
|
1883
1974
|
}
|
|
1884
1975
|
}
|
|
1885
1976
|
return { success: true };
|
|
1886
1977
|
});
|
|
1887
|
-
import_electron2.ipcMain.handle("sanqian-chat:hitlResponse", (
|
|
1978
|
+
import_electron2.ipcMain.handle("sanqian-chat:hitlResponse", (event, params) => {
|
|
1888
1979
|
const sdk = activeInstance2?.getSdk();
|
|
1889
|
-
const
|
|
1980
|
+
const senderWebContentsId = event.sender.id;
|
|
1981
|
+
const runId = activeInstance2?.resolveHitlRunId(
|
|
1982
|
+
{ runId: params.runId, streamId: params.streamId },
|
|
1983
|
+
senderWebContentsId
|
|
1984
|
+
) ?? null;
|
|
1890
1985
|
if (sdk && runId) {
|
|
1891
1986
|
sdk.sendHitlResponse(runId, params.response);
|
|
1987
|
+
} else if (params.streamId) {
|
|
1988
|
+
const stream = activeInstance2?.activeStreams.get(params.streamId);
|
|
1989
|
+
if (stream && !stream.cancelled && stream.ownerWebContentsId === senderWebContentsId) {
|
|
1990
|
+
stream.pendingHitlResponse = params.response;
|
|
1991
|
+
if (activeInstance2?.config.devMode) {
|
|
1992
|
+
console.warn("[ChatPanel] Queued HITL response while waiting for runId");
|
|
1993
|
+
}
|
|
1994
|
+
} else {
|
|
1995
|
+
console.warn("[ChatPanel] HITL response dropped: stream not found/cancelled/not-owned");
|
|
1996
|
+
}
|
|
1892
1997
|
} else if (activeInstance2?.config.devMode) {
|
|
1893
|
-
console.warn("[ChatPanel] HITL response dropped: missing runId");
|
|
1998
|
+
console.warn("[ChatPanel] HITL response dropped: missing or unauthorized runId");
|
|
1894
1999
|
}
|
|
1895
2000
|
return { success: true };
|
|
1896
2001
|
});
|
package/dist/main/index.mjs
CHANGED
|
@@ -359,6 +359,39 @@ function resolveHitlRunIdFromStreams(params, activeStreams) {
|
|
|
359
359
|
const first = activeRunIds.values().next();
|
|
360
360
|
return first.done ? null : first.value;
|
|
361
361
|
}
|
|
362
|
+
function resolveOwnedHitlRunIdFromStreams(params, activeStreams, senderWebContentsId) {
|
|
363
|
+
if (params.streamId) {
|
|
364
|
+
const stream = activeStreams.get(params.streamId);
|
|
365
|
+
if (!stream || stream.cancelled) {
|
|
366
|
+
return null;
|
|
367
|
+
}
|
|
368
|
+
if (stream.ownerWebContentsId !== senderWebContentsId) {
|
|
369
|
+
return null;
|
|
370
|
+
}
|
|
371
|
+
return stream.runId ?? null;
|
|
372
|
+
}
|
|
373
|
+
if (params.runId) {
|
|
374
|
+
for (const stream of activeStreams.values()) {
|
|
375
|
+
if (stream.cancelled || !stream.runId) continue;
|
|
376
|
+
if (stream.ownerWebContentsId !== senderWebContentsId) continue;
|
|
377
|
+
if (stream.runId === params.runId) {
|
|
378
|
+
return params.runId;
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
return null;
|
|
382
|
+
}
|
|
383
|
+
const activeRunIds = /* @__PURE__ */ new Set();
|
|
384
|
+
for (const stream of activeStreams.values()) {
|
|
385
|
+
if (stream.cancelled || !stream.runId) continue;
|
|
386
|
+
if (stream.ownerWebContentsId !== senderWebContentsId) continue;
|
|
387
|
+
activeRunIds.add(stream.runId);
|
|
388
|
+
if (activeRunIds.size > 1) {
|
|
389
|
+
return null;
|
|
390
|
+
}
|
|
391
|
+
}
|
|
392
|
+
const first = activeRunIds.values().next();
|
|
393
|
+
return first.done ? null : first.value;
|
|
394
|
+
}
|
|
362
395
|
|
|
363
396
|
// src/main/FloatingWindow.ts
|
|
364
397
|
var ipcHandlersRegistered = false;
|
|
@@ -394,7 +427,10 @@ var FloatingWindow = class _FloatingWindow {
|
|
|
394
427
|
app.whenReady().then(() => this.registerShortcut());
|
|
395
428
|
}
|
|
396
429
|
}
|
|
397
|
-
resolveHitlRunId(params) {
|
|
430
|
+
resolveHitlRunId(params, senderWebContentsId) {
|
|
431
|
+
if (typeof senderWebContentsId === "number") {
|
|
432
|
+
return resolveOwnedHitlRunIdFromStreams(params, this.activeStreams, senderWebContentsId);
|
|
433
|
+
}
|
|
398
434
|
return resolveHitlRunIdFromStreams(params, this.activeStreams);
|
|
399
435
|
}
|
|
400
436
|
/**
|
|
@@ -637,7 +673,13 @@ var FloatingWindow = class _FloatingWindow {
|
|
|
637
673
|
webContents.send("sanqian-chat:streamEvent", { streamId, event: { type: "error", error: "SDK or agent not ready" } });
|
|
638
674
|
return;
|
|
639
675
|
}
|
|
640
|
-
const streamState = {
|
|
676
|
+
const streamState = {
|
|
677
|
+
cancelled: false,
|
|
678
|
+
runId: null,
|
|
679
|
+
cancelSignalSent: false,
|
|
680
|
+
pendingHitlResponse: null,
|
|
681
|
+
ownerWebContentsId: webContents.id
|
|
682
|
+
};
|
|
641
683
|
activeInstance?.activeStreams.set(streamId, streamState);
|
|
642
684
|
try {
|
|
643
685
|
await sdk.ensureReady();
|
|
@@ -652,6 +694,14 @@ var FloatingWindow = class _FloatingWindow {
|
|
|
652
694
|
if (evtWithRunId.run_id && !streamState.runId) {
|
|
653
695
|
streamState.runId = evtWithRunId.run_id;
|
|
654
696
|
}
|
|
697
|
+
if (streamState.pendingHitlResponse && streamState.runId) {
|
|
698
|
+
try {
|
|
699
|
+
sdk.sendHitlResponse(streamState.runId, streamState.pendingHitlResponse);
|
|
700
|
+
streamState.pendingHitlResponse = null;
|
|
701
|
+
} catch (e) {
|
|
702
|
+
console.warn("[FloatingWindow] Failed to flush queued HITL response:", e);
|
|
703
|
+
}
|
|
704
|
+
}
|
|
655
705
|
if (streamState.cancelled && streamState.runId && !streamState.cancelSignalSent) {
|
|
656
706
|
try {
|
|
657
707
|
sdk.cancelRun(streamState.runId);
|
|
@@ -713,31 +763,50 @@ var FloatingWindow = class _FloatingWindow {
|
|
|
713
763
|
activeInstance?.activeStreams.delete(streamId);
|
|
714
764
|
}
|
|
715
765
|
});
|
|
716
|
-
ipcMain.handle("sanqian-chat:cancelStream", (
|
|
766
|
+
ipcMain.handle("sanqian-chat:cancelStream", (event, params) => {
|
|
717
767
|
const stream = activeInstance?.activeStreams.get(params.streamId);
|
|
718
|
-
if (stream) {
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
768
|
+
if (!stream) {
|
|
769
|
+
return { success: false, error: "stream_not_found" };
|
|
770
|
+
}
|
|
771
|
+
if (stream.ownerWebContentsId !== event.sender.id) {
|
|
772
|
+
console.warn("[FloatingWindow] Rejecting cancelStream from non-owner sender");
|
|
773
|
+
return { success: false, error: "stream_not_owned_by_sender" };
|
|
774
|
+
}
|
|
775
|
+
stream.cancelled = true;
|
|
776
|
+
if (stream.runId && !stream.cancelSignalSent) {
|
|
777
|
+
const sdk = activeInstance ? _FloatingWindow.getSdkFromOptions(activeInstance.options) : null;
|
|
778
|
+
if (sdk) {
|
|
779
|
+
try {
|
|
780
|
+
sdk.cancelRun(stream.runId);
|
|
781
|
+
stream.cancelSignalSent = true;
|
|
782
|
+
} catch (e) {
|
|
783
|
+
console.warn("[FloatingWindow] Failed to cancel run:", e);
|
|
729
784
|
}
|
|
730
785
|
}
|
|
731
786
|
}
|
|
732
787
|
return { success: true };
|
|
733
788
|
});
|
|
734
|
-
ipcMain.handle("sanqian-chat:hitlResponse", (
|
|
789
|
+
ipcMain.handle("sanqian-chat:hitlResponse", (event, params) => {
|
|
735
790
|
const sdk = activeInstance ? _FloatingWindow.getSdkFromOptions(activeInstance.options) : null;
|
|
736
|
-
const
|
|
791
|
+
const senderWebContentsId = event.sender.id;
|
|
792
|
+
const runId = activeInstance?.resolveHitlRunId(
|
|
793
|
+
{ runId: params.runId, streamId: params.streamId },
|
|
794
|
+
senderWebContentsId
|
|
795
|
+
) ?? null;
|
|
737
796
|
if (sdk && runId) {
|
|
738
797
|
sdk.sendHitlResponse(runId, params.response);
|
|
798
|
+
} else if (params.streamId) {
|
|
799
|
+
const stream = activeInstance?.activeStreams.get(params.streamId);
|
|
800
|
+
if (stream && !stream.cancelled && stream.ownerWebContentsId === senderWebContentsId) {
|
|
801
|
+
stream.pendingHitlResponse = params.response;
|
|
802
|
+
if (activeInstance?.options.devMode) {
|
|
803
|
+
console.warn("[FloatingWindow] Queued HITL response while waiting for runId");
|
|
804
|
+
}
|
|
805
|
+
} else {
|
|
806
|
+
console.warn("[FloatingWindow] HITL response dropped: stream not found/cancelled/not-owned");
|
|
807
|
+
}
|
|
739
808
|
} else if (activeInstance?.options.devMode) {
|
|
740
|
-
console.warn("[FloatingWindow] HITL response dropped: missing runId");
|
|
809
|
+
console.warn("[FloatingWindow] HITL response dropped: missing or unauthorized runId");
|
|
741
810
|
}
|
|
742
811
|
return { success: true };
|
|
743
812
|
});
|
|
@@ -1696,7 +1765,10 @@ var ChatPanel = class {
|
|
|
1696
1765
|
* Resolve HITL runId with deterministic binding for concurrent streams.
|
|
1697
1766
|
* Priority: explicit runId -> streamId-bound runId -> single active stream fallback.
|
|
1698
1767
|
*/
|
|
1699
|
-
resolveHitlRunId(params) {
|
|
1768
|
+
resolveHitlRunId(params, senderWebContentsId) {
|
|
1769
|
+
if (typeof senderWebContentsId === "number") {
|
|
1770
|
+
return resolveOwnedHitlRunIdFromStreams(params, this.activeStreams, senderWebContentsId);
|
|
1771
|
+
}
|
|
1700
1772
|
return resolveHitlRunIdFromStreams(params, this.activeStreams);
|
|
1701
1773
|
}
|
|
1702
1774
|
/**
|
|
@@ -1760,7 +1832,13 @@ var ChatPanel = class {
|
|
|
1760
1832
|
webContents.send("sanqian-chat:streamEvent", { streamId, event: { type: "error", error: "SDK or agent not ready" } });
|
|
1761
1833
|
return;
|
|
1762
1834
|
}
|
|
1763
|
-
const streamState = {
|
|
1835
|
+
const streamState = {
|
|
1836
|
+
cancelled: false,
|
|
1837
|
+
runId: null,
|
|
1838
|
+
cancelSignalSent: false,
|
|
1839
|
+
pendingHitlResponse: null,
|
|
1840
|
+
ownerWebContentsId: webContents.id
|
|
1841
|
+
};
|
|
1764
1842
|
activeInstance2?.activeStreams.set(streamId, streamState);
|
|
1765
1843
|
try {
|
|
1766
1844
|
await sdk.ensureReady();
|
|
@@ -1780,6 +1858,14 @@ var ChatPanel = class {
|
|
|
1780
1858
|
if (evtWithRunId.run_id && !streamState.runId) {
|
|
1781
1859
|
streamState.runId = evtWithRunId.run_id;
|
|
1782
1860
|
}
|
|
1861
|
+
if (streamState.pendingHitlResponse && streamState.runId) {
|
|
1862
|
+
try {
|
|
1863
|
+
sdk.sendHitlResponse(streamState.runId, streamState.pendingHitlResponse);
|
|
1864
|
+
streamState.pendingHitlResponse = null;
|
|
1865
|
+
} catch (e) {
|
|
1866
|
+
console.warn("[ChatPanel] Failed to flush queued HITL response:", e);
|
|
1867
|
+
}
|
|
1868
|
+
}
|
|
1783
1869
|
if (streamState.cancelled && streamState.runId && !streamState.cancelSignalSent) {
|
|
1784
1870
|
try {
|
|
1785
1871
|
sdk.cancelRun(streamState.runId);
|
|
@@ -1841,31 +1927,50 @@ var ChatPanel = class {
|
|
|
1841
1927
|
activeInstance2?.activeStreams.delete(streamId);
|
|
1842
1928
|
}
|
|
1843
1929
|
});
|
|
1844
|
-
ipcMain2.handle("sanqian-chat:cancelStream", (
|
|
1930
|
+
ipcMain2.handle("sanqian-chat:cancelStream", (event, params) => {
|
|
1845
1931
|
const stream = activeInstance2?.activeStreams.get(params.streamId);
|
|
1846
|
-
if (stream) {
|
|
1847
|
-
|
|
1848
|
-
|
|
1849
|
-
|
|
1850
|
-
|
|
1851
|
-
|
|
1852
|
-
|
|
1853
|
-
|
|
1854
|
-
|
|
1855
|
-
|
|
1856
|
-
|
|
1932
|
+
if (!stream) {
|
|
1933
|
+
return { success: false, error: "stream_not_found" };
|
|
1934
|
+
}
|
|
1935
|
+
if (stream.ownerWebContentsId !== event.sender.id) {
|
|
1936
|
+
console.warn("[ChatPanel] Rejecting cancelStream from non-owner sender");
|
|
1937
|
+
return { success: false, error: "stream_not_owned_by_sender" };
|
|
1938
|
+
}
|
|
1939
|
+
stream.cancelled = true;
|
|
1940
|
+
if (stream.runId && !stream.cancelSignalSent) {
|
|
1941
|
+
const sdk = activeInstance2?.getSdk();
|
|
1942
|
+
if (sdk) {
|
|
1943
|
+
try {
|
|
1944
|
+
sdk.cancelRun(stream.runId);
|
|
1945
|
+
stream.cancelSignalSent = true;
|
|
1946
|
+
} catch (e) {
|
|
1947
|
+
console.warn("[ChatPanel] Failed to cancel run:", e);
|
|
1857
1948
|
}
|
|
1858
1949
|
}
|
|
1859
1950
|
}
|
|
1860
1951
|
return { success: true };
|
|
1861
1952
|
});
|
|
1862
|
-
ipcMain2.handle("sanqian-chat:hitlResponse", (
|
|
1953
|
+
ipcMain2.handle("sanqian-chat:hitlResponse", (event, params) => {
|
|
1863
1954
|
const sdk = activeInstance2?.getSdk();
|
|
1864
|
-
const
|
|
1955
|
+
const senderWebContentsId = event.sender.id;
|
|
1956
|
+
const runId = activeInstance2?.resolveHitlRunId(
|
|
1957
|
+
{ runId: params.runId, streamId: params.streamId },
|
|
1958
|
+
senderWebContentsId
|
|
1959
|
+
) ?? null;
|
|
1865
1960
|
if (sdk && runId) {
|
|
1866
1961
|
sdk.sendHitlResponse(runId, params.response);
|
|
1962
|
+
} else if (params.streamId) {
|
|
1963
|
+
const stream = activeInstance2?.activeStreams.get(params.streamId);
|
|
1964
|
+
if (stream && !stream.cancelled && stream.ownerWebContentsId === senderWebContentsId) {
|
|
1965
|
+
stream.pendingHitlResponse = params.response;
|
|
1966
|
+
if (activeInstance2?.config.devMode) {
|
|
1967
|
+
console.warn("[ChatPanel] Queued HITL response while waiting for runId");
|
|
1968
|
+
}
|
|
1969
|
+
} else {
|
|
1970
|
+
console.warn("[ChatPanel] HITL response dropped: stream not found/cancelled/not-owned");
|
|
1971
|
+
}
|
|
1867
1972
|
} else if (activeInstance2?.config.devMode) {
|
|
1868
|
-
console.warn("[ChatPanel] HITL response dropped: missing runId");
|
|
1973
|
+
console.warn("[ChatPanel] HITL response dropped: missing or unauthorized runId");
|
|
1869
1974
|
}
|
|
1870
1975
|
return { success: true };
|
|
1871
1976
|
});
|
|
@@ -72,7 +72,8 @@ interface ChatUiConfigSerializable {
|
|
|
72
72
|
/** Font size scale: small (13px), normal (14px), large (16px), extra-large (18px) */
|
|
73
73
|
fontSize?: ChatFontSize;
|
|
74
74
|
accentColor?: string;
|
|
75
|
-
|
|
75
|
+
/** Supports Locale plus BCP-47 variants (e.g. zh-CN, en-US) */
|
|
76
|
+
locale?: Locale | string;
|
|
76
77
|
strings?: Partial<ChatUiStrings>;
|
|
77
78
|
alwaysOnTop?: boolean;
|
|
78
79
|
}
|
|
@@ -72,7 +72,8 @@ interface ChatUiConfigSerializable {
|
|
|
72
72
|
/** Font size scale: small (13px), normal (14px), large (16px), extra-large (18px) */
|
|
73
73
|
fontSize?: ChatFontSize;
|
|
74
74
|
accentColor?: string;
|
|
75
|
-
|
|
75
|
+
/** Supports Locale plus BCP-47 variants (e.g. zh-CN, en-US) */
|
|
76
|
+
locale?: Locale | string;
|
|
76
77
|
strings?: Partial<ChatUiStrings>;
|
|
77
78
|
alwaysOnTop?: boolean;
|
|
78
79
|
}
|
|
@@ -77,7 +77,8 @@ interface ChatUiConfigSerializable {
|
|
|
77
77
|
/** Font size scale: small (13px), normal (14px), large (16px), extra-large (18px) */
|
|
78
78
|
fontSize?: ChatFontSize;
|
|
79
79
|
accentColor?: string;
|
|
80
|
-
|
|
80
|
+
/** Supports Locale plus BCP-47 variants (e.g. zh-CN, en-US) */
|
|
81
|
+
locale?: Locale | string;
|
|
81
82
|
strings?: Partial<ChatUiStrings>;
|
|
82
83
|
alwaysOnTop?: boolean;
|
|
83
84
|
}
|
|
@@ -1231,7 +1232,7 @@ interface HistoryModalProps {
|
|
|
1231
1232
|
}
|
|
1232
1233
|
declare const HistoryModal: react.NamedExoticComponent<HistoryModalProps>;
|
|
1233
1234
|
|
|
1234
|
-
declare function resolveChatStrings(locale?: Locale, overrides?: Partial<ChatUiStrings>): ChatUiStrings;
|
|
1235
|
+
declare function resolveChatStrings(locale?: Locale | string, overrides?: Partial<ChatUiStrings>): ChatUiStrings;
|
|
1235
1236
|
|
|
1236
1237
|
type AlertType = 'warning' | 'error';
|
|
1237
1238
|
interface AlertAction {
|
package/dist/renderer/index.d.ts
CHANGED
|
@@ -77,7 +77,8 @@ interface ChatUiConfigSerializable {
|
|
|
77
77
|
/** Font size scale: small (13px), normal (14px), large (16px), extra-large (18px) */
|
|
78
78
|
fontSize?: ChatFontSize;
|
|
79
79
|
accentColor?: string;
|
|
80
|
-
|
|
80
|
+
/** Supports Locale plus BCP-47 variants (e.g. zh-CN, en-US) */
|
|
81
|
+
locale?: Locale | string;
|
|
81
82
|
strings?: Partial<ChatUiStrings>;
|
|
82
83
|
alwaysOnTop?: boolean;
|
|
83
84
|
}
|
|
@@ -1231,7 +1232,7 @@ interface HistoryModalProps {
|
|
|
1231
1232
|
}
|
|
1232
1233
|
declare const HistoryModal: react.NamedExoticComponent<HistoryModalProps>;
|
|
1233
1234
|
|
|
1234
|
-
declare function resolveChatStrings(locale?: Locale, overrides?: Partial<ChatUiStrings>): ChatUiStrings;
|
|
1235
|
+
declare function resolveChatStrings(locale?: Locale | string, overrides?: Partial<ChatUiStrings>): ChatUiStrings;
|
|
1235
1236
|
|
|
1236
1237
|
type AlertType = 'warning' | 'error';
|
|
1237
1238
|
interface AlertAction {
|