@blade-hq/agent-client 1.2.1-beta.9 → 2608.0.0
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 +102 -20
- package/dist/index.js.map +1 -1
- package/dist/session/agent-session.d.ts +9 -2
- package/dist/session/hub.d.ts +18 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -56,9 +56,14 @@ async function loginWithPopup(client, options = {}) {
|
|
|
56
56
|
return new Promise((resolve, reject) => {
|
|
57
57
|
const timeoutMs = options.timeoutMs ?? 5 * 60 * 1e3;
|
|
58
58
|
let settled = false;
|
|
59
|
+
let closeTimer;
|
|
60
|
+
const stopCloseWatch = () => {
|
|
61
|
+
globalThis.clearInterval(closeWatcher);
|
|
62
|
+
if (closeTimer !== void 0) globalThis.clearTimeout(closeTimer);
|
|
63
|
+
};
|
|
59
64
|
const cleanup = () => {
|
|
60
65
|
window.removeEventListener("message", onMessage);
|
|
61
|
-
|
|
66
|
+
stopCloseWatch();
|
|
62
67
|
globalThis.clearTimeout(timeoutHandle);
|
|
63
68
|
};
|
|
64
69
|
const settle = (fn) => {
|
|
@@ -73,6 +78,7 @@ async function loginWithPopup(client, options = {}) {
|
|
|
73
78
|
if (!isAuthCallbackMessage(event.data)) return;
|
|
74
79
|
if (event.data.state !== state) return;
|
|
75
80
|
const message = event.data;
|
|
81
|
+
stopCloseWatch();
|
|
76
82
|
try {
|
|
77
83
|
popup.close();
|
|
78
84
|
} catch {
|
|
@@ -98,11 +104,10 @@ async function loginWithPopup(client, options = {}) {
|
|
|
98
104
|
});
|
|
99
105
|
};
|
|
100
106
|
const closeWatcher = globalThis.setInterval(() => {
|
|
101
|
-
if (popup.closed
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
}
|
|
107
|
+
if (!popup.closed || settled || closeTimer !== void 0) return;
|
|
108
|
+
closeTimer = globalThis.setTimeout(() => {
|
|
109
|
+
settle(() => reject(new Error("\u767B\u5F55\u7A97\u53E3\u5DF2\u5173\u95ED\uFF0C\u672A\u5B8C\u6210\u6388\u6743\u3002")));
|
|
110
|
+
}, 400);
|
|
106
111
|
}, 500);
|
|
107
112
|
const timeoutHandle = globalThis.setTimeout(() => {
|
|
108
113
|
settle(() => reject(new Error("\u767B\u5F55\u8D85\u65F6\uFF0C\u8BF7\u91CD\u8BD5\u3002")));
|
|
@@ -1611,7 +1616,9 @@ function projectHistory(entries) {
|
|
|
1611
1616
|
const toolOwners = /* @__PURE__ */ new Map();
|
|
1612
1617
|
const latestAssistantByLoop = /* @__PURE__ */ new Map();
|
|
1613
1618
|
const pendingChildPauses = /* @__PURE__ */ new Map();
|
|
1619
|
+
const answeredToolCallIds = /* @__PURE__ */ new Set();
|
|
1614
1620
|
let latestRootMessage = null;
|
|
1621
|
+
let latestRootAssistant = null;
|
|
1615
1622
|
let sequence = 0;
|
|
1616
1623
|
for (const entry of entries) {
|
|
1617
1624
|
const kind = String(entry.kind ?? "");
|
|
@@ -1670,6 +1677,10 @@ function projectHistory(entries) {
|
|
|
1670
1677
|
}
|
|
1671
1678
|
turns.push(turn);
|
|
1672
1679
|
if (role === "assistant") latestAssistantByLoop.set(loopId, turn);
|
|
1680
|
+
if (loopId === "root") {
|
|
1681
|
+
if (role === "assistant") latestRootAssistant = turn;
|
|
1682
|
+
else if (role === "user") latestRootAssistant = null;
|
|
1683
|
+
}
|
|
1673
1684
|
for (const toolCall of toolCalls) toolOwners.set(toolCall.id, turn);
|
|
1674
1685
|
continue;
|
|
1675
1686
|
}
|
|
@@ -1764,6 +1775,7 @@ function projectHistory(entries) {
|
|
|
1764
1775
|
} else if (kind === "ask_user_answer") {
|
|
1765
1776
|
const data = entry.data ?? {};
|
|
1766
1777
|
const toolCallId = String(data.tool_call_id ?? "");
|
|
1778
|
+
if (toolCallId) answeredToolCallIds.add(toolCallId);
|
|
1767
1779
|
const pending = pendingChildPauses.get(toolCallId);
|
|
1768
1780
|
if (pending) {
|
|
1769
1781
|
setToolCallStatus(latestAssistantByLoop.get(pending.childLoopId), toolCallId, "done", null, null);
|
|
@@ -1790,8 +1802,44 @@ function projectHistory(entries) {
|
|
|
1790
1802
|
);
|
|
1791
1803
|
}
|
|
1792
1804
|
}
|
|
1805
|
+
const rootAssistant = latestRootAssistant;
|
|
1806
|
+
if (rootAssistant) {
|
|
1807
|
+
for (const toolCall of rootAssistant.tool_calls) {
|
|
1808
|
+
if (answeredToolCallIds.has(toolCall.id)) continue;
|
|
1809
|
+
if (toolCall.tool_name.split(":").pop() !== "AskUserQuestion") continue;
|
|
1810
|
+
if (toolCall.status === "error" || toolCall.status === "cancelled") continue;
|
|
1811
|
+
const pauseArguments = parseToolArguments(toolCall.arguments);
|
|
1812
|
+
if (typeof toolCall.result === "string") {
|
|
1813
|
+
const resultArguments = parseToolArguments(toolCall.result);
|
|
1814
|
+
if (Array.isArray(resultArguments.questions)) {
|
|
1815
|
+
pauseArguments.questions = resultArguments.questions;
|
|
1816
|
+
}
|
|
1817
|
+
}
|
|
1818
|
+
applyAskUserPauseToTurn(
|
|
1819
|
+
rootAssistant,
|
|
1820
|
+
{
|
|
1821
|
+
name: toolCall.tool_name,
|
|
1822
|
+
tool_call_id: toolCall.id,
|
|
1823
|
+
arguments: pauseArguments
|
|
1824
|
+
},
|
|
1825
|
+
toolCall.id,
|
|
1826
|
+
"",
|
|
1827
|
+
"",
|
|
1828
|
+
(name) => name
|
|
1829
|
+
);
|
|
1830
|
+
}
|
|
1831
|
+
}
|
|
1793
1832
|
return turns;
|
|
1794
1833
|
}
|
|
1834
|
+
function parseToolArguments(value) {
|
|
1835
|
+
if (!value) return {};
|
|
1836
|
+
try {
|
|
1837
|
+
const parsed = JSON.parse(value);
|
|
1838
|
+
return isRecord3(parsed) ? parsed : {};
|
|
1839
|
+
} catch {
|
|
1840
|
+
return {};
|
|
1841
|
+
}
|
|
1842
|
+
}
|
|
1795
1843
|
function buildToolCalls(value) {
|
|
1796
1844
|
if (!Array.isArray(value)) return [];
|
|
1797
1845
|
return value.filter(isRecord3).map((raw) => {
|
|
@@ -3118,7 +3166,7 @@ var AgentSession = class _AgentSession {
|
|
|
3118
3166
|
countFiles: async () => readonlyError(),
|
|
3119
3167
|
downloadFile: async () => readonlyError(),
|
|
3120
3168
|
attachAppCli: async () => readonlyError(),
|
|
3121
|
-
onDispose: () =>
|
|
3169
|
+
onDispose: () => true
|
|
3122
3170
|
},
|
|
3123
3171
|
"connected"
|
|
3124
3172
|
);
|
|
@@ -3376,15 +3424,18 @@ var AgentSession = class _AgentSession {
|
|
|
3376
3424
|
downloadFile(filePath, downloadName) {
|
|
3377
3425
|
return this.runtime.downloadFile(this.sessionId, filePath, downloadName);
|
|
3378
3426
|
}
|
|
3379
|
-
/**
|
|
3427
|
+
/**
|
|
3428
|
+
* 归还一份持有。同一个会话被多处 connect 时按引用计数释放:只有最后
|
|
3429
|
+
* 一份归还才真正断开订阅与监听器,之前的归还不影响仍在使用的一方。
|
|
3430
|
+
*/
|
|
3380
3431
|
dispose() {
|
|
3381
3432
|
if (this.disposed) return;
|
|
3433
|
+
if (!this.runtime.onDispose(this)) return;
|
|
3382
3434
|
this.disposed = true;
|
|
3383
3435
|
this.cancelPatchFlush();
|
|
3384
3436
|
this.listeners.clear();
|
|
3385
3437
|
this.emitter.clear();
|
|
3386
3438
|
this.commands.clear();
|
|
3387
|
-
this.runtime.onDispose(this);
|
|
3388
3439
|
}
|
|
3389
3440
|
get isDisposed() {
|
|
3390
3441
|
return this.disposed;
|
|
@@ -3834,6 +3885,8 @@ var SessionHub = class {
|
|
|
3834
3885
|
client;
|
|
3835
3886
|
sessions = /* @__PURE__ */ new Map();
|
|
3836
3887
|
bootstrapPending = /* @__PURE__ */ new Map();
|
|
3888
|
+
/** 每个会话当前有多少方持有(connect 一次 +1,dispose 一次 -1)。 */
|
|
3889
|
+
holdCounts = /* @__PURE__ */ new Map();
|
|
3837
3890
|
connection = "disconnected";
|
|
3838
3891
|
connectionListeners = /* @__PURE__ */ new Set();
|
|
3839
3892
|
bound = false;
|
|
@@ -3852,22 +3905,30 @@ var SessionHub = class {
|
|
|
3852
3905
|
this.connectionListeners.delete(listener);
|
|
3853
3906
|
};
|
|
3854
3907
|
}
|
|
3855
|
-
/**
|
|
3908
|
+
/**
|
|
3909
|
+
* 取得(或复用)一个会话的 AgentSession,并完成历史加载 + 房间订阅。
|
|
3910
|
+
*
|
|
3911
|
+
* 每次调用都算一份持有,调用方用完必须 `dispose()` 归还。复用同一实例时
|
|
3912
|
+
* 只有最后一份归还才退订房间——否则 StrictMode 双跑、路由抖动这类"连两次
|
|
3913
|
+
* 只释放一次"的场景会把仍在用的会话退订掉,之后所有 turn/chat 事件静默丢失。
|
|
3914
|
+
*/
|
|
3856
3915
|
async connect(sessionId) {
|
|
3857
3916
|
const existing = this.sessions.get(sessionId);
|
|
3858
3917
|
if (existing && !existing.isDisposed) {
|
|
3918
|
+
this.hold(sessionId);
|
|
3859
3919
|
return this.bootstrapPending.get(sessionId) ?? existing;
|
|
3860
3920
|
}
|
|
3861
3921
|
this.ensureBound();
|
|
3862
3922
|
const session = new AgentSession(sessionId, this.runtimeFor(), this.connection);
|
|
3863
3923
|
this.sessions.set(sessionId, session);
|
|
3924
|
+
this.hold(sessionId);
|
|
3864
3925
|
this.ensureConnected();
|
|
3865
3926
|
const bootstrap = session._bootstrap().then(() => session);
|
|
3866
3927
|
this.bootstrapPending.set(sessionId, bootstrap);
|
|
3867
3928
|
try {
|
|
3868
3929
|
return await bootstrap;
|
|
3869
3930
|
} catch (error) {
|
|
3870
|
-
|
|
3931
|
+
this.forceRelease(session);
|
|
3871
3932
|
throw error;
|
|
3872
3933
|
} finally {
|
|
3873
3934
|
if (this.bootstrapPending.get(sessionId) === bootstrap) {
|
|
@@ -3898,7 +3959,7 @@ var SessionHub = class {
|
|
|
3898
3959
|
disposeAll() {
|
|
3899
3960
|
this.bootstrapPending.clear();
|
|
3900
3961
|
for (const session of [...this.sessions.values()]) {
|
|
3901
|
-
|
|
3962
|
+
this.forceRelease(session);
|
|
3902
3963
|
}
|
|
3903
3964
|
}
|
|
3904
3965
|
/**
|
|
@@ -3935,15 +3996,36 @@ var SessionHub = class {
|
|
|
3935
3996
|
countFiles: (sessionId, dirPath) => this.client.sessions.countFiles(sessionId, dirPath),
|
|
3936
3997
|
downloadFile: (sessionId, filePath, downloadName) => this.client.sessions.downloadFile(sessionId, filePath, downloadName),
|
|
3937
3998
|
attachAppCli: (sessionId, definition) => this.client.sessions.attachAppCli(sessionId, definition),
|
|
3938
|
-
onDispose: (session) =>
|
|
3939
|
-
if (this.sessions.get(session.sessionId) === session) {
|
|
3940
|
-
this.sessions.delete(session.sessionId);
|
|
3941
|
-
}
|
|
3942
|
-
;
|
|
3943
|
-
this.client.socket().emit("session:unsubscribe", { session_id: session.sessionId });
|
|
3944
|
-
}
|
|
3999
|
+
onDispose: (session) => this.release(session)
|
|
3945
4000
|
};
|
|
3946
4001
|
}
|
|
4002
|
+
/** 记一份持有。 */
|
|
4003
|
+
hold(sessionId) {
|
|
4004
|
+
this.holdCounts.set(sessionId, (this.holdCounts.get(sessionId) ?? 0) + 1);
|
|
4005
|
+
}
|
|
4006
|
+
/**
|
|
4007
|
+
* 归还一份持有;还剩别的持有者时返回 false,让 AgentSession 保持可用。
|
|
4008
|
+
* 归零才退订房间并从路由表移除。
|
|
4009
|
+
*/
|
|
4010
|
+
release(session) {
|
|
4011
|
+
const remaining = (this.holdCounts.get(session.sessionId) ?? 0) - 1;
|
|
4012
|
+
if (remaining > 0) {
|
|
4013
|
+
this.holdCounts.set(session.sessionId, remaining);
|
|
4014
|
+
return false;
|
|
4015
|
+
}
|
|
4016
|
+
this.holdCounts.delete(session.sessionId);
|
|
4017
|
+
if (this.sessions.get(session.sessionId) === session) {
|
|
4018
|
+
this.sessions.delete(session.sessionId);
|
|
4019
|
+
}
|
|
4020
|
+
;
|
|
4021
|
+
this.client.socket().emit("session:unsubscribe", { session_id: session.sessionId });
|
|
4022
|
+
return true;
|
|
4023
|
+
}
|
|
4024
|
+
/** 不管还有几方持有都立即释放(启动失败、切换身份)。 */
|
|
4025
|
+
forceRelease(session) {
|
|
4026
|
+
this.holdCounts.delete(session.sessionId);
|
|
4027
|
+
session.dispose();
|
|
4028
|
+
}
|
|
3947
4029
|
ensureConnected() {
|
|
3948
4030
|
const socket = this.client.socket();
|
|
3949
4031
|
if (socket.connected || socket.active) return;
|
|
@@ -4131,7 +4213,7 @@ function resolveAuthToken(options) {
|
|
|
4131
4213
|
|
|
4132
4214
|
// src/version.ts
|
|
4133
4215
|
var SDK_NAME = "agent-client";
|
|
4134
|
-
var SDK_VERSION = true ? "
|
|
4216
|
+
var SDK_VERSION = true ? "2608.0.0" : "1.1.1";
|
|
4135
4217
|
|
|
4136
4218
|
// src/socket.ts
|
|
4137
4219
|
function withSdkIdentity(auth) {
|