@borgee/agents-host 0.2.44 → 0.2.56
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 +23 -27
- package/dist/agents-host.d.ts +26 -5
- package/dist/agents-host.js +163 -192
- package/dist/chat/chat-control-plane.d.ts +3 -0
- package/dist/chat/sdk-chat-control-plane.d.ts +4 -3
- package/dist/chat/sdk-chat-control-plane.js +3 -0
- package/dist/cli.js +1 -5
- package/dist/compatibility-gates.d.ts +4 -0
- package/dist/compatibility-gates.js +18 -1
- package/dist/context/claude-file-brief.d.ts +2 -0
- package/dist/context/claude-file-brief.js +83 -0
- package/dist/context/compaction.d.ts +20 -0
- package/dist/context/compaction.js +59 -0
- package/dist/context/injection.d.ts +39 -6
- package/dist/context/injection.js +317 -26
- package/dist/context/main-session-delegation.d.ts +1 -1
- package/dist/context/projection-strategy.d.ts +24 -0
- package/dist/context/projection-strategy.js +90 -0
- package/dist/context/prompt.d.ts +16 -1
- package/dist/context/prompt.js +456 -22
- package/dist/context/resolved-workspace.d.ts +2 -0
- package/dist/context/resolved-workspace.js +64 -0
- package/dist/context/skill-manual.d.ts +1 -0
- package/dist/context/skill-manual.js +4 -1
- package/dist/context/turn-preparation.d.ts +8 -2
- package/dist/context/turn-preparation.js +56 -14
- package/dist/gateway/localhost-gateway.js +2 -0
- package/dist/managed-daemon.js +122 -9
- package/dist/plugin-sdk.js +276 -359
- package/dist/plugin-sdk.js.map +4 -4
- package/dist/progress-to-activity.d.ts +16 -0
- package/dist/progress-to-activity.js +24 -0
- package/dist/projection-strategy-values.d.ts +4 -0
- package/dist/projection-strategy-values.js +28 -0
- package/dist/providers/acp-progress-collector.d.ts +44 -0
- package/dist/providers/acp-progress-collector.js +130 -0
- package/dist/providers/awaiting-user.d.ts +2 -3
- package/dist/providers/awaiting-user.js +5 -7
- package/dist/providers/claude/activity-metadata.d.ts +14 -0
- package/dist/providers/claude/activity-metadata.js +81 -0
- package/dist/providers/claude/cli-client.d.ts +1 -2
- package/dist/providers/claude/cli-client.js +190 -117
- package/dist/providers/codex/cli-client.js +3 -83
- package/dist/providers/codex/project-doc.js +12 -11
- package/dist/providers/copilot/cli-client.js +3 -83
- package/dist/providers/create-provider.d.ts +2 -0
- package/dist/providers/create-provider.js +20 -4
- package/dist/state-paths.d.ts +1 -1
- package/dist/state-paths.js +3 -3
- package/dist/task-thread-resolution.d.ts +3 -2
- package/dist/types.d.ts +130 -6
- package/package.json +2 -2
- package/skills/borgee-agent/SKILL.md +9 -1
- package/skills/borgee-agent/references/task-properties.md +5 -2
- package/dist/durable-cursor-store.d.ts +0 -5
- package/dist/durable-cursor-store.js +0 -7
package/dist/plugin-sdk.js
CHANGED
|
@@ -3224,7 +3224,7 @@ var require_stream = __commonJS({
|
|
|
3224
3224
|
};
|
|
3225
3225
|
duplex._final = function(callback) {
|
|
3226
3226
|
if (ws.readyState === ws.CONNECTING) {
|
|
3227
|
-
ws.once("open", function
|
|
3227
|
+
ws.once("open", function open() {
|
|
3228
3228
|
duplex._final(callback);
|
|
3229
3229
|
});
|
|
3230
3230
|
return;
|
|
@@ -3245,7 +3245,7 @@ var require_stream = __commonJS({
|
|
|
3245
3245
|
};
|
|
3246
3246
|
duplex._write = function(chunk, encoding, callback) {
|
|
3247
3247
|
if (ws.readyState === ws.CONNECTING) {
|
|
3248
|
-
ws.once("open", function
|
|
3248
|
+
ws.once("open", function open() {
|
|
3249
3249
|
duplex._write(chunk, encoding, callback);
|
|
3250
3250
|
});
|
|
3251
3251
|
return;
|
|
@@ -3706,123 +3706,8 @@ var require_websocket_server = __commonJS({
|
|
|
3706
3706
|
}
|
|
3707
3707
|
});
|
|
3708
3708
|
|
|
3709
|
-
// ../sdk/plugin-ts/dist/cursor-store.js
|
|
3710
|
-
import { randomUUID } from "node:crypto";
|
|
3711
|
-
import { mkdir, open, readFile, rename, unlink } from "node:fs/promises";
|
|
3712
|
-
import { dirname } from "node:path";
|
|
3713
|
-
var MemoryCursorStore = class {
|
|
3714
|
-
cursors = /* @__PURE__ */ new Map();
|
|
3715
|
-
async read(agentId) {
|
|
3716
|
-
return this.cursors.get(agentId) ?? -1;
|
|
3717
|
-
}
|
|
3718
|
-
async write(agentId, cursor) {
|
|
3719
|
-
this.cursors.set(agentId, cursor);
|
|
3720
|
-
}
|
|
3721
|
-
};
|
|
3722
|
-
var nodeCursorFileOps = {
|
|
3723
|
-
mkdir: (path) => mkdir(path, { recursive: true }),
|
|
3724
|
-
open: (path, flags, mode) => open(path, flags, mode),
|
|
3725
|
-
rename,
|
|
3726
|
-
unlink
|
|
3727
|
-
};
|
|
3728
|
-
var FileCursorStore = class {
|
|
3729
|
-
resolvePathFn;
|
|
3730
|
-
fs;
|
|
3731
|
-
platform;
|
|
3732
|
-
constructor(options) {
|
|
3733
|
-
this.resolvePathFn = options.resolvePath;
|
|
3734
|
-
this.fs = options.fs ?? nodeCursorFileOps;
|
|
3735
|
-
this.platform = options.platform ?? process.platform;
|
|
3736
|
-
}
|
|
3737
|
-
async read(agentId) {
|
|
3738
|
-
const filePath = this.resolvePathFn(agentId);
|
|
3739
|
-
let raw;
|
|
3740
|
-
try {
|
|
3741
|
-
raw = await readFile(filePath, "utf8");
|
|
3742
|
-
} catch (error) {
|
|
3743
|
-
if (error.code === "ENOENT") {
|
|
3744
|
-
return -1;
|
|
3745
|
-
}
|
|
3746
|
-
throw error;
|
|
3747
|
-
}
|
|
3748
|
-
const parsed = JSON.parse(raw);
|
|
3749
|
-
if (!Number.isSafeInteger(parsed.cursor) || parsed.cursor < 0) {
|
|
3750
|
-
throw new Error(`invalid cursor file: ${filePath}`);
|
|
3751
|
-
}
|
|
3752
|
-
return parsed.cursor;
|
|
3753
|
-
}
|
|
3754
|
-
async write(agentId, cursor) {
|
|
3755
|
-
if (!Number.isSafeInteger(cursor) || cursor < 0) {
|
|
3756
|
-
throw new Error(`invalid cursor: ${cursor}`);
|
|
3757
|
-
}
|
|
3758
|
-
const filePath = this.resolvePathFn(agentId);
|
|
3759
|
-
const parentPath = dirname(filePath);
|
|
3760
|
-
await this.fs.mkdir(parentPath);
|
|
3761
|
-
const temporaryPath = `${filePath}.${process.pid}.${randomUUID()}.tmp`;
|
|
3762
|
-
let temporaryHandle;
|
|
3763
|
-
try {
|
|
3764
|
-
temporaryHandle = await this.fs.open(temporaryPath, "wx", 384);
|
|
3765
|
-
await temporaryHandle.writeFile(JSON.stringify({ cursor, updatedAt: Date.now() }), {
|
|
3766
|
-
encoding: "utf8"
|
|
3767
|
-
});
|
|
3768
|
-
await temporaryHandle.sync();
|
|
3769
|
-
await temporaryHandle.close();
|
|
3770
|
-
temporaryHandle = void 0;
|
|
3771
|
-
await this.fs.rename(temporaryPath, filePath);
|
|
3772
|
-
await this.syncDirectory(parentPath);
|
|
3773
|
-
} catch (error) {
|
|
3774
|
-
if (temporaryHandle) {
|
|
3775
|
-
try {
|
|
3776
|
-
await temporaryHandle.close();
|
|
3777
|
-
} catch {
|
|
3778
|
-
}
|
|
3779
|
-
}
|
|
3780
|
-
try {
|
|
3781
|
-
await this.fs.unlink(temporaryPath);
|
|
3782
|
-
} catch {
|
|
3783
|
-
}
|
|
3784
|
-
throw error;
|
|
3785
|
-
}
|
|
3786
|
-
}
|
|
3787
|
-
shouldIgnoreDirectorySyncError(error) {
|
|
3788
|
-
const code = error.code;
|
|
3789
|
-
return this.platform === "win32" && (code === "EPERM" || code === "EINVAL");
|
|
3790
|
-
}
|
|
3791
|
-
async syncDirectory(path) {
|
|
3792
|
-
let directoryHandle;
|
|
3793
|
-
try {
|
|
3794
|
-
try {
|
|
3795
|
-
directoryHandle = await this.fs.open(path, "r");
|
|
3796
|
-
} catch (error) {
|
|
3797
|
-
if (!this.shouldIgnoreDirectorySyncError(error)) {
|
|
3798
|
-
throw error;
|
|
3799
|
-
}
|
|
3800
|
-
return;
|
|
3801
|
-
}
|
|
3802
|
-
try {
|
|
3803
|
-
await directoryHandle.sync();
|
|
3804
|
-
} catch (error) {
|
|
3805
|
-
if (!this.shouldIgnoreDirectorySyncError(error)) {
|
|
3806
|
-
throw error;
|
|
3807
|
-
}
|
|
3808
|
-
}
|
|
3809
|
-
} catch (error) {
|
|
3810
|
-
if (directoryHandle) {
|
|
3811
|
-
try {
|
|
3812
|
-
await directoryHandle.close();
|
|
3813
|
-
} catch {
|
|
3814
|
-
}
|
|
3815
|
-
}
|
|
3816
|
-
throw error;
|
|
3817
|
-
}
|
|
3818
|
-
if (directoryHandle) {
|
|
3819
|
-
await directoryHandle.close();
|
|
3820
|
-
}
|
|
3821
|
-
}
|
|
3822
|
-
};
|
|
3823
|
-
|
|
3824
3709
|
// ../sdk/plugin-ts/dist/bpp/bpp-transport.js
|
|
3825
|
-
import { randomUUID
|
|
3710
|
+
import { randomUUID } from "node:crypto";
|
|
3826
3711
|
|
|
3827
3712
|
// ../../node_modules/.pnpm/ws@8.21.1/node_modules/ws/wrapper.mjs
|
|
3828
3713
|
var import_stream = __toESM(require_stream(), 1);
|
|
@@ -3869,32 +3754,33 @@ var CONFIG_ACK_REASONS = /* @__PURE__ */ new Set([
|
|
|
3869
3754
|
"unknown"
|
|
3870
3755
|
]);
|
|
3871
3756
|
var WS_CLOSE_AUTH_FAILED = 4004;
|
|
3757
|
+
var RESUME_REPLAY_MODE = "latest_n";
|
|
3758
|
+
var RESUME_SINCE_CURSOR = 0;
|
|
3759
|
+
var RESUME_LATEST_N = 200;
|
|
3872
3760
|
var RESULT_STATUS_OK = "ok";
|
|
3761
|
+
var STOP_TURN_ACTION = "stop_turn";
|
|
3762
|
+
var STOP_TURN_CHANNEL_ID_INVALID = "stop_turn_channel_id_invalid";
|
|
3763
|
+
var SERVER_REQUEST_FAILED = "server_request_failed";
|
|
3873
3764
|
var BppTransport = class {
|
|
3874
3765
|
performTimeoutMs;
|
|
3875
3766
|
heartbeatIntervalMs;
|
|
3876
3767
|
reconnectBaseMs;
|
|
3877
3768
|
reconnectMaxMs;
|
|
3878
3769
|
autoReconnect;
|
|
3879
|
-
resumeAckTimeoutMs;
|
|
3880
3770
|
configApplyTimeoutMs;
|
|
3881
3771
|
ctx;
|
|
3882
3772
|
handlers;
|
|
3883
3773
|
serverRequestHandler;
|
|
3774
|
+
stopTurnHandler;
|
|
3884
3775
|
ws;
|
|
3885
3776
|
pending = /* @__PURE__ */ new Map();
|
|
3886
|
-
legacyAckSockets = /* @__PURE__ */ new WeakSet();
|
|
3887
|
-
legacyReconnectSockets = /* @__PURE__ */ new WeakSet();
|
|
3888
3777
|
closed = false;
|
|
3889
|
-
// ONLINE =
|
|
3890
|
-
//
|
|
3778
|
+
// ONLINE = this socket's handshake has been written and its identity is known.
|
|
3779
|
+
// Outbound semantic actions gate on this rather than on a raw open socket, so a
|
|
3780
|
+
// send issued during a reconnect never races ahead of the handshake.
|
|
3891
3781
|
online = false;
|
|
3892
3782
|
terminal = false;
|
|
3893
|
-
|
|
3894
|
-
replayRemaining = 0;
|
|
3895
|
-
replayHighWater = -1;
|
|
3896
|
-
lastConsumedCursor = -1;
|
|
3897
|
-
cursorFailed = false;
|
|
3783
|
+
inboundFailed = false;
|
|
3898
3784
|
inboundChain = Promise.resolve();
|
|
3899
3785
|
configChain = Promise.resolve();
|
|
3900
3786
|
configAcks = /* @__PURE__ */ new Map();
|
|
@@ -3903,7 +3789,8 @@ var BppTransport = class {
|
|
|
3903
3789
|
reconnectAttempt = 0;
|
|
3904
3790
|
heartbeatTimer;
|
|
3905
3791
|
reconnectTimer;
|
|
3906
|
-
//
|
|
3792
|
+
// The agent id resolved by get_me when the caller supplied none; stamped on
|
|
3793
|
+
// every outbound frame that names an agent.
|
|
3907
3794
|
agentId = "";
|
|
3908
3795
|
constructor(options = {}) {
|
|
3909
3796
|
this.performTimeoutMs = options.performTimeoutMs ?? 3e4;
|
|
@@ -3911,15 +3798,11 @@ var BppTransport = class {
|
|
|
3911
3798
|
this.reconnectBaseMs = options.reconnectBaseMs ?? 500;
|
|
3912
3799
|
this.reconnectMaxMs = options.reconnectMaxMs ?? 3e4;
|
|
3913
3800
|
this.autoReconnect = options.autoReconnect ?? true;
|
|
3914
|
-
this.resumeAckTimeoutMs = options.resumeAckTimeoutMs ?? 5e3;
|
|
3915
3801
|
this.configApplyTimeoutMs = options.configApplyTimeoutMs ?? 5e3;
|
|
3916
3802
|
}
|
|
3917
3803
|
get logger() {
|
|
3918
3804
|
return this.ctx?.logger;
|
|
3919
3805
|
}
|
|
3920
|
-
get cursorStore() {
|
|
3921
|
-
return this.ctx?.cursorStore;
|
|
3922
|
-
}
|
|
3923
3806
|
currentAgentId() {
|
|
3924
3807
|
return this.agentId || this.ctx?.agentId || "";
|
|
3925
3808
|
}
|
|
@@ -3929,6 +3812,9 @@ var BppTransport = class {
|
|
|
3929
3812
|
setServerRequestHandler(handler) {
|
|
3930
3813
|
this.serverRequestHandler = handler;
|
|
3931
3814
|
}
|
|
3815
|
+
setStopTurnHandler(handler) {
|
|
3816
|
+
this.stopTurnHandler = handler;
|
|
3817
|
+
}
|
|
3932
3818
|
getResolvedAgentId() {
|
|
3933
3819
|
return this.currentAgentId();
|
|
3934
3820
|
}
|
|
@@ -3948,8 +3834,6 @@ var BppTransport = class {
|
|
|
3948
3834
|
this.online = false;
|
|
3949
3835
|
this.stopHeartbeat();
|
|
3950
3836
|
this.rejectReconnectWaiters(new BorgeeError("transport not connected", "bpp.not_connected"));
|
|
3951
|
-
if (this.resumeWaiter)
|
|
3952
|
-
this.rejectResumeWaiter(this.resumeWaiter.ws, new BorgeeError("transport closed", "bpp.transport_closed"));
|
|
3953
3837
|
this.rejectAllPending(new BorgeeError("transport closed", "bpp.transport_closed"));
|
|
3954
3838
|
const ws = this.ws;
|
|
3955
3839
|
this.ws = void 0;
|
|
@@ -3974,17 +3858,21 @@ var BppTransport = class {
|
|
|
3974
3858
|
this.sendTyping(action);
|
|
3975
3859
|
return void 0;
|
|
3976
3860
|
}
|
|
3861
|
+
if (action.op === "report_activity") {
|
|
3862
|
+
this.sendActivity(action);
|
|
3863
|
+
return void 0;
|
|
3864
|
+
}
|
|
3977
3865
|
const deadline = Date.now() + this.performTimeoutMs;
|
|
3978
3866
|
if (this.closed || this.terminal) {
|
|
3979
3867
|
throw new BorgeeError("transport not connected", "bpp.not_connected");
|
|
3980
3868
|
}
|
|
3981
|
-
if (opts?.
|
|
3869
|
+
if (opts?.handshake) {
|
|
3982
3870
|
if (!this.isOpen())
|
|
3983
3871
|
throw new BorgeeError("transport not connected", "bpp.not_connected");
|
|
3984
3872
|
} else if (!this.online) {
|
|
3985
3873
|
await this.waitUntilOnline(action.op, deadline);
|
|
3986
3874
|
}
|
|
3987
|
-
const nonce =
|
|
3875
|
+
const nonce = randomUUID();
|
|
3988
3876
|
const frame = {
|
|
3989
3877
|
type: "semantic_action",
|
|
3990
3878
|
agent_id: this.currentAgentId(),
|
|
@@ -4004,7 +3892,7 @@ var BppTransport = class {
|
|
|
4004
3892
|
}
|
|
4005
3893
|
// ---- socket 生命周期 ----
|
|
4006
3894
|
// Transition the given socket to ONLINE. Bails if the socket is stale (a reconnect replaced it),
|
|
4007
|
-
// so a
|
|
3895
|
+
// so a handshake completing on a prior socket cannot flip online across a reconnect.
|
|
4008
3896
|
goOnline(source) {
|
|
4009
3897
|
if (!source || source !== this.ws)
|
|
4010
3898
|
return;
|
|
@@ -4015,7 +3903,7 @@ var BppTransport = class {
|
|
|
4015
3903
|
isOpen() {
|
|
4016
3904
|
return this.ws !== void 0 && this.ws.readyState === import_websocket.default.OPEN;
|
|
4017
3905
|
}
|
|
4018
|
-
openSocket(
|
|
3906
|
+
openSocket() {
|
|
4019
3907
|
const ctx = this.ctx;
|
|
4020
3908
|
if (!ctx)
|
|
4021
3909
|
throw new BorgeeError("connect() called without context", "bpp.no_context");
|
|
@@ -4043,7 +3931,7 @@ var BppTransport = class {
|
|
|
4043
3931
|
settled = true;
|
|
4044
3932
|
reject(error);
|
|
4045
3933
|
}
|
|
4046
|
-
}
|
|
3934
|
+
});
|
|
4047
3935
|
});
|
|
4048
3936
|
ws.on("message", (data) => {
|
|
4049
3937
|
this.onMessage(rawToString(data), ws);
|
|
@@ -4056,11 +3944,7 @@ var BppTransport = class {
|
|
|
4056
3944
|
}
|
|
4057
3945
|
});
|
|
4058
3946
|
ws.on("close", (code, reason) => {
|
|
4059
|
-
const legacyReconnect = this.legacyReconnectSockets.has(ws);
|
|
4060
|
-
if (legacyReconnect)
|
|
4061
|
-
this.legacyReconnectSockets.delete(ws);
|
|
4062
3947
|
connectionAbort.abort();
|
|
4063
|
-
this.rejectResumeWaiter(ws, new BorgeeError("ws closed before resume acknowledgement", "bpp.resume_incomplete"));
|
|
4064
3948
|
const wasCurrent = this.ws === ws;
|
|
4065
3949
|
if (wasCurrent) {
|
|
4066
3950
|
this.online = false;
|
|
@@ -4077,11 +3961,11 @@ var BppTransport = class {
|
|
|
4077
3961
|
if (wasCurrent) {
|
|
4078
3962
|
this.rejectAllPending(authFailed ? new BorgeeError("authentication failed: invalid or revoked API key", "bpp.auth_failed") : new BorgeeError("connection lost", "bpp.connection_lost"));
|
|
4079
3963
|
}
|
|
4080
|
-
if (!settled
|
|
3964
|
+
if (!settled) {
|
|
4081
3965
|
settled = true;
|
|
4082
|
-
reject(authFailed ? new BorgeeError("authentication failed: invalid or revoked API key", "bpp.auth_failed") : new BorgeeError(opened ? "ws closed before
|
|
3966
|
+
reject(authFailed ? new BorgeeError("authentication failed: invalid or revoked API key", "bpp.auth_failed") : new BorgeeError(opened ? "ws closed before the handshake was written" : "ws closed before open", opened ? "bpp.handshake_incomplete" : "bpp.connect_failed"));
|
|
4083
3967
|
}
|
|
4084
|
-
if (authFailed ||
|
|
3968
|
+
if (authFailed || !wasCurrent) {
|
|
4085
3969
|
if (authFailed && wasCurrent) {
|
|
4086
3970
|
this.rejectReconnectWaiters(new BorgeeError("authentication failed: invalid or revoked API key", "bpp.auth_failed"));
|
|
4087
3971
|
}
|
|
@@ -4095,7 +3979,7 @@ var BppTransport = class {
|
|
|
4095
3979
|
});
|
|
4096
3980
|
});
|
|
4097
3981
|
}
|
|
4098
|
-
async onOpen(ws, resolve, reject
|
|
3982
|
+
async onOpen(ws, resolve, reject) {
|
|
4099
3983
|
const ctx = this.ctx;
|
|
4100
3984
|
if (!ctx)
|
|
4101
3985
|
return;
|
|
@@ -4109,154 +3993,33 @@ var BppTransport = class {
|
|
|
4109
3993
|
this.sendTo(ws, connectFrame);
|
|
4110
3994
|
try {
|
|
4111
3995
|
if (!this.currentAgentId()) {
|
|
4112
|
-
const identity = await this.perform({ op: "get_me" }, {
|
|
3996
|
+
const identity = await this.perform({ op: "get_me" }, { handshake: true });
|
|
4113
3997
|
const resolved = identity && typeof identity === "object" ? identity.id : "";
|
|
4114
3998
|
if (!resolved)
|
|
4115
3999
|
throw new BorgeeError("authenticated agent identity could not be resolved", "bpp.identity_unresolved");
|
|
4116
4000
|
this.agentId = resolved;
|
|
4117
4001
|
}
|
|
4118
|
-
|
|
4119
|
-
this.
|
|
4120
|
-
|
|
4121
|
-
|
|
4122
|
-
return;
|
|
4123
|
-
}
|
|
4124
|
-
this.sendResumeRequest(ws, Math.max(cursor, 0), resolve, reject);
|
|
4002
|
+
this.sendResumeRequest(ws);
|
|
4003
|
+
this.startHeartbeat();
|
|
4004
|
+
this.goOnline(ws);
|
|
4005
|
+
resolve();
|
|
4125
4006
|
} catch (error) {
|
|
4126
|
-
const failure = error instanceof BorgeeError ? error : new BorgeeError(`
|
|
4007
|
+
const failure = error instanceof BorgeeError ? error : new BorgeeError(`agent identity handshake failed: ${String(error)}`, "bpp.identity_unresolved");
|
|
4127
4008
|
reject(failure);
|
|
4128
4009
|
this.failTerminal(failure, ws);
|
|
4129
4010
|
}
|
|
4130
4011
|
}
|
|
4131
|
-
|
|
4132
|
-
|
|
4133
|
-
|
|
4134
|
-
|
|
4135
|
-
const replayMode = ctx.replayMode ?? "latest_n";
|
|
4012
|
+
// The resume is written for one reader: a server that still holds durable
|
|
4013
|
+
// frames behind a pre-ack barrier releases it when this frame arrives. Its
|
|
4014
|
+
// answer is not read, so the request asks for nothing.
|
|
4015
|
+
sendResumeRequest(ws) {
|
|
4136
4016
|
const frame = {
|
|
4137
4017
|
type: "session.resume",
|
|
4138
|
-
replay_mode:
|
|
4139
|
-
since_cursor:
|
|
4140
|
-
|
|
4141
|
-
};
|
|
4142
|
-
this.handlers?.onStateChange({ status: "resuming" });
|
|
4143
|
-
const priorWaiter = this.resumeWaiter;
|
|
4144
|
-
if (priorWaiter?.ws === ws) {
|
|
4145
|
-
clearTimeout(priorWaiter.timer);
|
|
4146
|
-
}
|
|
4147
|
-
const timeoutMs = ctx.resumeAckTimeoutMs ?? this.resumeAckTimeoutMs;
|
|
4148
|
-
let waiter;
|
|
4149
|
-
const timer = setTimeout(() => {
|
|
4150
|
-
if (this.resumeWaiter !== waiter || this.ws !== ws)
|
|
4151
|
-
return;
|
|
4152
|
-
this.resumeWaiter = void 0;
|
|
4153
|
-
if (this.retryWithLegacyReconnect(ws, resolve, reject))
|
|
4154
|
-
return;
|
|
4155
|
-
const error = new BorgeeError("session.resume_ack timed out", "bpp.resume_ack_timeout");
|
|
4156
|
-
reject(error);
|
|
4157
|
-
this.failTerminal(error, ws);
|
|
4158
|
-
}, timeoutMs);
|
|
4159
|
-
waiter = { ws, resolve, reject, timer };
|
|
4160
|
-
this.resumeWaiter = waiter;
|
|
4161
|
-
this.send(frame);
|
|
4162
|
-
}
|
|
4163
|
-
sendLegacyReconnect(ws, sinceCursor, resolve) {
|
|
4164
|
-
const ctx = this.ctx;
|
|
4165
|
-
if (!ctx || ws !== this.ws || ws.readyState !== import_websocket.default.OPEN)
|
|
4166
|
-
return;
|
|
4167
|
-
this.legacyAckSockets.add(ws);
|
|
4168
|
-
const now = Date.now();
|
|
4169
|
-
const frame = {
|
|
4170
|
-
type: "reconnect_handshake",
|
|
4171
|
-
plugin_id: ctx.pluginId ?? "",
|
|
4172
|
-
agent_id: this.currentAgentId(),
|
|
4173
|
-
last_known_cursor: sinceCursor,
|
|
4174
|
-
disconnect_at: now,
|
|
4175
|
-
reconnect_at: now
|
|
4018
|
+
replay_mode: RESUME_REPLAY_MODE,
|
|
4019
|
+
since_cursor: RESUME_SINCE_CURSOR,
|
|
4020
|
+
latest_n: RESUME_LATEST_N
|
|
4176
4021
|
};
|
|
4177
|
-
this.replayRemaining = 0;
|
|
4178
|
-
this.replayHighWater = Math.max(this.replayHighWater, sinceCursor);
|
|
4179
4022
|
this.sendTo(ws, frame);
|
|
4180
|
-
resolve();
|
|
4181
|
-
this.startHeartbeat();
|
|
4182
|
-
this.goOnline(ws);
|
|
4183
|
-
}
|
|
4184
|
-
retryWithLegacyReconnect(ws, resolve, reject) {
|
|
4185
|
-
if (ws !== this.ws || ws.readyState !== import_websocket.default.OPEN)
|
|
4186
|
-
return false;
|
|
4187
|
-
this.legacyReconnectSockets.add(ws);
|
|
4188
|
-
try {
|
|
4189
|
-
ws.close();
|
|
4190
|
-
} catch {
|
|
4191
|
-
this.legacyReconnectSockets.delete(ws);
|
|
4192
|
-
return false;
|
|
4193
|
-
}
|
|
4194
|
-
void this.openSocket("legacy").then(resolve).catch(reject);
|
|
4195
|
-
return true;
|
|
4196
|
-
}
|
|
4197
|
-
finishReplayIfReady(source) {
|
|
4198
|
-
if (this.replayRemaining !== 0)
|
|
4199
|
-
return Promise.resolve();
|
|
4200
|
-
const highWater = this.replayHighWater;
|
|
4201
|
-
if (highWater <= this.lastConsumedCursor) {
|
|
4202
|
-
this.goOnline(source);
|
|
4203
|
-
return Promise.resolve();
|
|
4204
|
-
}
|
|
4205
|
-
return this.persistCursor(highWater).then(() => {
|
|
4206
|
-
this.goOnline(source);
|
|
4207
|
-
});
|
|
4208
|
-
}
|
|
4209
|
-
handleResumeAck(frame, source) {
|
|
4210
|
-
const waiter = this.resumeWaiter;
|
|
4211
|
-
const compatibilityAck = source === this.ws && this.legacyAckSockets.has(source) && (!waiter || waiter.ws !== source);
|
|
4212
|
-
if (!compatibilityAck && (!waiter || waiter.ws !== source || source !== this.ws))
|
|
4213
|
-
return;
|
|
4214
|
-
if (waiter?.ws === source)
|
|
4215
|
-
clearTimeout(waiter.timer);
|
|
4216
|
-
if (!Number.isSafeInteger(frame.count) || frame.count < 0 || frame.count > 500 || !Number.isSafeInteger(frame.high_water) || frame.high_water < 0) {
|
|
4217
|
-
if (waiter?.ws === source)
|
|
4218
|
-
this.resumeWaiter = void 0;
|
|
4219
|
-
if (compatibilityAck)
|
|
4220
|
-
this.legacyAckSockets.delete(source);
|
|
4221
|
-
const error = new BorgeeError("invalid session.resume_ack bounds", "bpp.resume_ack_invalid");
|
|
4222
|
-
if (waiter?.ws === source)
|
|
4223
|
-
waiter.reject(error);
|
|
4224
|
-
this.failTerminal(error, source);
|
|
4225
|
-
return;
|
|
4226
|
-
}
|
|
4227
|
-
if (frame.reset) {
|
|
4228
|
-
if (waiter?.ws === source)
|
|
4229
|
-
this.resumeWaiter = void 0;
|
|
4230
|
-
void this.resetCursor(frame.high_water).then(() => {
|
|
4231
|
-
if (compatibilityAck)
|
|
4232
|
-
this.legacyAckSockets.delete(source);
|
|
4233
|
-
this.replayRemaining = 0;
|
|
4234
|
-
this.replayHighWater = frame.high_water;
|
|
4235
|
-
waiter?.resolve();
|
|
4236
|
-
this.startHeartbeat();
|
|
4237
|
-
this.inboundChain = this.inboundChain.then(() => this.finishReplayIfReady(source)).catch((error) => {
|
|
4238
|
-
this.failTerminal(this.cursorFailure(error), source);
|
|
4239
|
-
});
|
|
4240
|
-
}).catch((error) => {
|
|
4241
|
-
const failure = this.cursorFailure(error);
|
|
4242
|
-
waiter?.reject(failure);
|
|
4243
|
-
this.failTerminal(failure, source);
|
|
4244
|
-
});
|
|
4245
|
-
return;
|
|
4246
|
-
}
|
|
4247
|
-
if (waiter?.ws === source)
|
|
4248
|
-
this.resumeWaiter = void 0;
|
|
4249
|
-
if (compatibilityAck)
|
|
4250
|
-
this.legacyAckSockets.delete(source);
|
|
4251
|
-
this.replayRemaining = frame.count;
|
|
4252
|
-
this.replayHighWater = frame.high_water;
|
|
4253
|
-
waiter?.resolve();
|
|
4254
|
-
this.startHeartbeat();
|
|
4255
|
-
if (frame.count === 0) {
|
|
4256
|
-
this.inboundChain = this.inboundChain.then(() => this.finishReplayIfReady(source)).catch((error) => {
|
|
4257
|
-
this.failTerminal(this.cursorFailure(error), source);
|
|
4258
|
-
});
|
|
4259
|
-
}
|
|
4260
4023
|
}
|
|
4261
4024
|
scheduleReconnect() {
|
|
4262
4025
|
if (this.closed || this.terminal || !this.autoReconnect)
|
|
@@ -4312,13 +4075,7 @@ var BppTransport = class {
|
|
|
4312
4075
|
void this.handleServerRequest(env);
|
|
4313
4076
|
break;
|
|
4314
4077
|
case "inbound_message":
|
|
4315
|
-
this.handleInbound(env
|
|
4316
|
-
break;
|
|
4317
|
-
case "session.resume_ack":
|
|
4318
|
-
this.handleResumeAck(env, source);
|
|
4319
|
-
break;
|
|
4320
|
-
case "session.summary":
|
|
4321
|
-
this.handleSummary(env, source);
|
|
4078
|
+
this.handleInbound(env);
|
|
4322
4079
|
break;
|
|
4323
4080
|
case "semantic_action_result":
|
|
4324
4081
|
this.handleActionResult(env);
|
|
@@ -4329,6 +4086,7 @@ var BppTransport = class {
|
|
|
4329
4086
|
case "agent_config_update":
|
|
4330
4087
|
this.handleConfigUpdate(env, source);
|
|
4331
4088
|
break;
|
|
4089
|
+
case "session.resume_ack":
|
|
4332
4090
|
case "pong":
|
|
4333
4091
|
case "ping":
|
|
4334
4092
|
case "agent_toggle":
|
|
@@ -4337,40 +4095,14 @@ var BppTransport = class {
|
|
|
4337
4095
|
break;
|
|
4338
4096
|
}
|
|
4339
4097
|
}
|
|
4340
|
-
handleInbound(frame
|
|
4341
|
-
this.inboundChain = this.inboundChain.then(
|
|
4342
|
-
if (this.
|
|
4098
|
+
handleInbound(frame) {
|
|
4099
|
+
this.inboundChain = this.inboundChain.then(() => {
|
|
4100
|
+
if (this.inboundFailed)
|
|
4343
4101
|
return;
|
|
4344
|
-
|
|
4345
|
-
throw new BorgeeError(`non-monotonic inbound cursor ${frame.cursor}`, "bpp.cursor_non_monotonic");
|
|
4346
|
-
}
|
|
4347
|
-
const event = mapInbound(frame);
|
|
4348
|
-
this.handlers?.onInbound(event);
|
|
4349
|
-
await this.persistCursor(frame.cursor);
|
|
4350
|
-
if (this.replayRemaining > 0) {
|
|
4351
|
-
this.replayRemaining -= 1;
|
|
4352
|
-
await this.finishReplayIfReady(source);
|
|
4353
|
-
}
|
|
4354
|
-
}).catch((error) => {
|
|
4355
|
-
this.cursorFailed = true;
|
|
4356
|
-
this.failTerminal(this.cursorFailure(error), this.ws);
|
|
4357
|
-
});
|
|
4358
|
-
}
|
|
4359
|
-
handleSummary(frame, source) {
|
|
4360
|
-
this.inboundChain = this.inboundChain.then(async () => {
|
|
4361
|
-
if (this.cursorFailed)
|
|
4362
|
-
return;
|
|
4363
|
-
if (!Number.isSafeInteger(frame.cursor) || frame.cursor < this.lastConsumedCursor)
|
|
4364
|
-
throw new BorgeeError(`non-monotonic summary cursor ${frame.cursor}`, "bpp.cursor_non_monotonic");
|
|
4365
|
-
this.handlers?.onReplaySummary({ cursor: frame.cursor, missedCount: frame.missed_count, sinceCursor: frame.since_cursor, throughCursor: frame.through_cursor, summary: frame.summary });
|
|
4366
|
-
await this.persistCursor(frame.cursor);
|
|
4367
|
-
if (this.replayRemaining > 0) {
|
|
4368
|
-
this.replayRemaining -= 1;
|
|
4369
|
-
await this.finishReplayIfReady(source);
|
|
4370
|
-
}
|
|
4102
|
+
this.handlers?.onInbound(mapInbound(frame));
|
|
4371
4103
|
}).catch((error) => {
|
|
4372
|
-
this.
|
|
4373
|
-
this.failTerminal(this.
|
|
4104
|
+
this.inboundFailed = true;
|
|
4105
|
+
this.failTerminal(this.inboundFailure(error), this.ws);
|
|
4374
4106
|
});
|
|
4375
4107
|
}
|
|
4376
4108
|
handleActionResult(frame) {
|
|
@@ -4380,7 +4112,7 @@ var BppTransport = class {
|
|
|
4380
4112
|
this.pending.delete(frame.nonce);
|
|
4381
4113
|
clearTimeout(p.timer);
|
|
4382
4114
|
if (frame.status === RESULT_STATUS_OK) {
|
|
4383
|
-
const result = decodeActionResult(p.op, frame.payload
|
|
4115
|
+
const result = decodeActionResult(p.op, frame.payload);
|
|
4384
4116
|
if (p.op === "get_me" && result && typeof result === "object") {
|
|
4385
4117
|
const id = result.id;
|
|
4386
4118
|
if (id)
|
|
@@ -4509,6 +4241,10 @@ var BppTransport = class {
|
|
|
4509
4241
|
return;
|
|
4510
4242
|
const data = env.data ?? {};
|
|
4511
4243
|
const action = typeof data.action === "string" ? data.action : "";
|
|
4244
|
+
if (action === STOP_TURN_ACTION) {
|
|
4245
|
+
await this.answerStopTurn(id, data);
|
|
4246
|
+
return;
|
|
4247
|
+
}
|
|
4512
4248
|
if (!this.serverRequestHandler) {
|
|
4513
4249
|
this.send({ type: "response", id, error: "no_server_request_handler" });
|
|
4514
4250
|
return;
|
|
@@ -4520,6 +4256,40 @@ var BppTransport = class {
|
|
|
4520
4256
|
this.send({ type: "response", id, error: err instanceof Error ? err.message : String(err) });
|
|
4521
4257
|
}
|
|
4522
4258
|
}
|
|
4259
|
+
// Precedence: the specific handler wins, an onServerRequest callback gets
|
|
4260
|
+
// stop_turn only while no specific one is registered, and with neither the
|
|
4261
|
+
// SDK answers the request itself. That order is what lets a plugin that
|
|
4262
|
+
// already switches on the action name inside one callback keep working
|
|
4263
|
+
// unchanged, and lets it move onto onStopTurn one action at a time.
|
|
4264
|
+
async answerStopTurn(id, data) {
|
|
4265
|
+
const channelId = data.channel_id;
|
|
4266
|
+
if (typeof channelId !== "string" || channelId === "") {
|
|
4267
|
+
this.send({ type: "response", id, error: STOP_TURN_CHANNEL_ID_INVALID });
|
|
4268
|
+
return;
|
|
4269
|
+
}
|
|
4270
|
+
const specific = this.stopTurnHandler;
|
|
4271
|
+
const generic = this.serverRequestHandler;
|
|
4272
|
+
try {
|
|
4273
|
+
const answer = specific ? await specific(channelId) : generic ? await generic(STOP_TURN_ACTION, data) : { aborted: false };
|
|
4274
|
+
this.send({ type: "response", id, data: this.asStopTurnResult(answer) });
|
|
4275
|
+
} catch (err) {
|
|
4276
|
+
this.logger?.warn("bpp.stop_turn_handler_failed", err);
|
|
4277
|
+
this.send({ type: "response", id, error: SERVER_REQUEST_FAILED });
|
|
4278
|
+
}
|
|
4279
|
+
}
|
|
4280
|
+
// The rail's answer is one boolean and nothing else, so that is what goes on
|
|
4281
|
+
// the wire whatever the handler returned. A callback switching on action names
|
|
4282
|
+
// answers actions it does not implement with something else entirely — and
|
|
4283
|
+
// "the callback does not implement stopping" is one of the things false
|
|
4284
|
+
// already covers, so it is reported as such rather than as a broken agent.
|
|
4285
|
+
asStopTurnResult(answer) {
|
|
4286
|
+
const aborted = answer?.aborted;
|
|
4287
|
+
if (typeof aborted !== "boolean") {
|
|
4288
|
+
this.logger?.warn("bpp.stop_turn_answer_unusable", answer);
|
|
4289
|
+
return { aborted: false };
|
|
4290
|
+
}
|
|
4291
|
+
return { aborted };
|
|
4292
|
+
}
|
|
4523
4293
|
// ---- task 生命周期上行 ----
|
|
4524
4294
|
sendTaskStarted(action) {
|
|
4525
4295
|
const frame = {
|
|
@@ -4544,6 +4314,59 @@ var BppTransport = class {
|
|
|
4544
4314
|
};
|
|
4545
4315
|
this.send(frame);
|
|
4546
4316
|
}
|
|
4317
|
+
// sendActivity flattens the four-shape union onto one frame. The union is
|
|
4318
|
+
// how a producer says which shape it is reporting; the wire puts the
|
|
4319
|
+
// discriminator beside its payload with no wrapper object, matching both the
|
|
4320
|
+
// protocol this relays and inbound_message's own `kind`.
|
|
4321
|
+
//
|
|
4322
|
+
// The frame names no agent: identity is stamped by the server from the
|
|
4323
|
+
// authenticated connection, so nothing here can report as another agent.
|
|
4324
|
+
sendActivity(action) {
|
|
4325
|
+
const frame = {
|
|
4326
|
+
type: "agent_activity",
|
|
4327
|
+
channel_id: action.channelId,
|
|
4328
|
+
sequence: action.sequence,
|
|
4329
|
+
shape: action.activity.shape,
|
|
4330
|
+
activity_id: "",
|
|
4331
|
+
label: "",
|
|
4332
|
+
description: "",
|
|
4333
|
+
kind: "",
|
|
4334
|
+
status: "",
|
|
4335
|
+
detail: "",
|
|
4336
|
+
parent_id: "",
|
|
4337
|
+
subagent: false,
|
|
4338
|
+
reason: "",
|
|
4339
|
+
stream: "",
|
|
4340
|
+
text: "",
|
|
4341
|
+
turn_state: ""
|
|
4342
|
+
};
|
|
4343
|
+
switch (action.activity.shape) {
|
|
4344
|
+
case "activity":
|
|
4345
|
+
frame.activity_id = action.activity.id;
|
|
4346
|
+
frame.label = action.activity.label ?? "";
|
|
4347
|
+
frame.description = action.activity.description ?? "";
|
|
4348
|
+
frame.kind = action.activity.kind ?? "";
|
|
4349
|
+
frame.status = action.activity.status ?? "";
|
|
4350
|
+
frame.detail = action.activity.detail ?? "";
|
|
4351
|
+
frame.parent_id = action.activity.parentId ?? "";
|
|
4352
|
+
frame.subagent = action.activity.subagent ?? false;
|
|
4353
|
+
frame.reason = action.activity.reason ?? "";
|
|
4354
|
+
if (action.activity.paths)
|
|
4355
|
+
frame.paths = action.activity.paths;
|
|
4356
|
+
break;
|
|
4357
|
+
case "plan":
|
|
4358
|
+
frame.plan = action.activity.entries.map((entry) => ({ label: entry.label, status: entry.status }));
|
|
4359
|
+
break;
|
|
4360
|
+
case "output":
|
|
4361
|
+
frame.stream = action.activity.stream;
|
|
4362
|
+
frame.text = action.activity.text;
|
|
4363
|
+
break;
|
|
4364
|
+
case "turn":
|
|
4365
|
+
frame.turn_state = action.activity.state;
|
|
4366
|
+
break;
|
|
4367
|
+
}
|
|
4368
|
+
this.send(frame);
|
|
4369
|
+
}
|
|
4547
4370
|
sendTyping(action) {
|
|
4548
4371
|
this.send({ type: "typing", channel_id: action.channelId });
|
|
4549
4372
|
}
|
|
@@ -4566,28 +4389,10 @@ var BppTransport = class {
|
|
|
4566
4389
|
this.logger?.warn("bpp.send_failed", err);
|
|
4567
4390
|
}
|
|
4568
4391
|
}
|
|
4569
|
-
|
|
4570
|
-
if (cursor <= this.lastConsumedCursor)
|
|
4571
|
-
return;
|
|
4572
|
-
await this.cursorStore?.write(this.currentAgentId(), cursor);
|
|
4573
|
-
this.lastConsumedCursor = cursor;
|
|
4574
|
-
}
|
|
4575
|
-
async resetCursor(cursor) {
|
|
4576
|
-
await this.cursorStore?.write(this.currentAgentId(), cursor);
|
|
4577
|
-
this.lastConsumedCursor = cursor;
|
|
4578
|
-
}
|
|
4579
|
-
cursorFailure(error) {
|
|
4392
|
+
inboundFailure(error) {
|
|
4580
4393
|
if (error instanceof BorgeeError)
|
|
4581
4394
|
return error;
|
|
4582
|
-
return new BorgeeError(`
|
|
4583
|
-
}
|
|
4584
|
-
rejectResumeWaiter(ws, error) {
|
|
4585
|
-
const waiter = this.resumeWaiter;
|
|
4586
|
-
if (!waiter || waiter.ws !== ws)
|
|
4587
|
-
return;
|
|
4588
|
-
clearTimeout(waiter.timer);
|
|
4589
|
-
this.resumeWaiter = void 0;
|
|
4590
|
-
waiter.reject(error);
|
|
4395
|
+
return new BorgeeError(`inbound dispatch failed: ${String(error)}`, "bpp.inbound_dispatch_failed");
|
|
4591
4396
|
}
|
|
4592
4397
|
failTerminal(error, ws) {
|
|
4593
4398
|
if (this.terminal)
|
|
@@ -4595,8 +4400,6 @@ var BppTransport = class {
|
|
|
4595
4400
|
this.terminal = true;
|
|
4596
4401
|
this.online = false;
|
|
4597
4402
|
this.handlers?.onStateChange({ status: "error", reason: "unknown", code: error.code });
|
|
4598
|
-
if (ws)
|
|
4599
|
-
this.rejectResumeWaiter(ws, error);
|
|
4600
4403
|
this.rejectReconnectWaiters(error);
|
|
4601
4404
|
this.rejectAllPending(error);
|
|
4602
4405
|
try {
|
|
@@ -4739,11 +4542,11 @@ function encodeActionPayload(action) {
|
|
|
4739
4542
|
return {};
|
|
4740
4543
|
}
|
|
4741
4544
|
}
|
|
4742
|
-
function decodeActionResult(op, payloadJSON
|
|
4545
|
+
function decodeActionResult(op, payloadJSON) {
|
|
4743
4546
|
const p = safeParseJSON(payloadJSON);
|
|
4744
4547
|
switch (op) {
|
|
4745
4548
|
case "send_message":
|
|
4746
|
-
return { messageId: String(p.message_id ?? "")
|
|
4549
|
+
return { messageId: String(p.message_id ?? "") };
|
|
4747
4550
|
case "create_dm":
|
|
4748
4551
|
return { channelId: String(p.channel_id ?? "") };
|
|
4749
4552
|
case "get_me":
|
|
@@ -4856,7 +4659,6 @@ function mapInbound(frame) {
|
|
|
4856
4659
|
const kind = frame.kind;
|
|
4857
4660
|
const base = {
|
|
4858
4661
|
kind,
|
|
4859
|
-
cursor: frame.cursor,
|
|
4860
4662
|
channelId: frame.channel_id,
|
|
4861
4663
|
channelType: frame.channel_type,
|
|
4862
4664
|
authorName: frame.author_name,
|
|
@@ -4931,6 +4733,102 @@ function rawToString(data) {
|
|
|
4931
4733
|
return String(data);
|
|
4932
4734
|
}
|
|
4933
4735
|
|
|
4736
|
+
// ../sdk/plugin-ts/dist/activity-reporter.js
|
|
4737
|
+
var DEFAULT_THROTTLE_MS = 150;
|
|
4738
|
+
var OUTPUT_TAIL_CHARS = 2e3;
|
|
4739
|
+
var defaultScheduler = (callback, delayMs) => {
|
|
4740
|
+
const timer = setTimeout(callback, delayMs);
|
|
4741
|
+
timer.unref?.();
|
|
4742
|
+
return () => clearTimeout(timer);
|
|
4743
|
+
};
|
|
4744
|
+
function outputTail(text) {
|
|
4745
|
+
if (text.length <= OUTPUT_TAIL_CHARS) {
|
|
4746
|
+
return text;
|
|
4747
|
+
}
|
|
4748
|
+
const tail = text.slice(text.length - OUTPUT_TAIL_CHARS);
|
|
4749
|
+
return /^[\uDC00-\uDFFF]/.test(tail) ? tail.slice(1) : tail;
|
|
4750
|
+
}
|
|
4751
|
+
function throttleKey(activity) {
|
|
4752
|
+
switch (activity.shape) {
|
|
4753
|
+
case "activity":
|
|
4754
|
+
return `activity:${activity.id}`;
|
|
4755
|
+
case "plan":
|
|
4756
|
+
return "plan";
|
|
4757
|
+
case "output":
|
|
4758
|
+
return `output:${activity.stream}`;
|
|
4759
|
+
}
|
|
4760
|
+
}
|
|
4761
|
+
var TurnActivityReporter = class {
|
|
4762
|
+
options;
|
|
4763
|
+
windows = /* @__PURE__ */ new Map();
|
|
4764
|
+
throttleMs;
|
|
4765
|
+
scheduler;
|
|
4766
|
+
ended = false;
|
|
4767
|
+
constructor(options) {
|
|
4768
|
+
this.options = options;
|
|
4769
|
+
this.throttleMs = options.throttleMs ?? DEFAULT_THROTTLE_MS;
|
|
4770
|
+
this.scheduler = options.scheduler ?? defaultScheduler;
|
|
4771
|
+
}
|
|
4772
|
+
start() {
|
|
4773
|
+
this.report({ shape: "turn", state: "started" });
|
|
4774
|
+
}
|
|
4775
|
+
progress(activity) {
|
|
4776
|
+
if (this.ended) {
|
|
4777
|
+
return;
|
|
4778
|
+
}
|
|
4779
|
+
this.offer(activity.shape === "output" ? { ...activity, text: outputTail(activity.text) } : activity);
|
|
4780
|
+
}
|
|
4781
|
+
/**
|
|
4782
|
+
* Ends the turn. What the windows still hold goes first, so the last thing a
|
|
4783
|
+
* reader sees of a turn is that turn's own last word rather than whatever
|
|
4784
|
+
* happened to fall outside a window.
|
|
4785
|
+
*/
|
|
4786
|
+
end() {
|
|
4787
|
+
if (this.ended) {
|
|
4788
|
+
return;
|
|
4789
|
+
}
|
|
4790
|
+
this.ended = true;
|
|
4791
|
+
this.drain();
|
|
4792
|
+
this.report({ shape: "turn", state: "ended" });
|
|
4793
|
+
}
|
|
4794
|
+
offer(activity) {
|
|
4795
|
+
const key = throttleKey(activity);
|
|
4796
|
+
const open = this.windows.get(key);
|
|
4797
|
+
if (open) {
|
|
4798
|
+
open.latest = activity;
|
|
4799
|
+
return;
|
|
4800
|
+
}
|
|
4801
|
+
this.report(activity);
|
|
4802
|
+
this.openWindow(key);
|
|
4803
|
+
}
|
|
4804
|
+
openWindow(key) {
|
|
4805
|
+
const window = { latest: null, cancel: null };
|
|
4806
|
+
window.cancel = this.scheduler(() => {
|
|
4807
|
+
this.windows.delete(key);
|
|
4808
|
+
if (window.latest) {
|
|
4809
|
+
this.offer(window.latest);
|
|
4810
|
+
}
|
|
4811
|
+
}, this.throttleMs);
|
|
4812
|
+
this.windows.set(key, window);
|
|
4813
|
+
}
|
|
4814
|
+
drain() {
|
|
4815
|
+
const held = [...this.windows.values()];
|
|
4816
|
+
this.windows.clear();
|
|
4817
|
+
for (const window of held) {
|
|
4818
|
+
window.cancel?.();
|
|
4819
|
+
if (window.latest) {
|
|
4820
|
+
this.report(window.latest);
|
|
4821
|
+
}
|
|
4822
|
+
}
|
|
4823
|
+
}
|
|
4824
|
+
report(activity) {
|
|
4825
|
+
if (this.options.canReport && !this.options.canReport()) {
|
|
4826
|
+
return;
|
|
4827
|
+
}
|
|
4828
|
+
this.options.deliver({ channelId: this.options.channelId, activity });
|
|
4829
|
+
}
|
|
4830
|
+
};
|
|
4831
|
+
|
|
4934
4832
|
// ../sdk/plugin-ts/dist/client.js
|
|
4935
4833
|
var noopLogger = {
|
|
4936
4834
|
debug() {
|
|
@@ -4946,21 +4844,19 @@ var Client = class {
|
|
|
4946
4844
|
opts;
|
|
4947
4845
|
t;
|
|
4948
4846
|
logger;
|
|
4949
|
-
cursorStore;
|
|
4950
4847
|
_agentId;
|
|
4848
|
+
activitySequence = 0;
|
|
4951
4849
|
_state = { status: "connecting" };
|
|
4952
4850
|
listeners = {
|
|
4953
4851
|
message: /* @__PURE__ */ new Set(),
|
|
4954
4852
|
configUpdate: /* @__PURE__ */ new Set(),
|
|
4955
4853
|
permissionDenied: /* @__PURE__ */ new Set(),
|
|
4956
|
-
connectionState: /* @__PURE__ */ new Set()
|
|
4957
|
-
replaySummary: /* @__PURE__ */ new Set()
|
|
4854
|
+
connectionState: /* @__PURE__ */ new Set()
|
|
4958
4855
|
};
|
|
4959
4856
|
constructor(opts, transport) {
|
|
4960
4857
|
this.opts = opts;
|
|
4961
4858
|
this.t = transport;
|
|
4962
4859
|
this.logger = opts.logger ?? noopLogger;
|
|
4963
|
-
this.cursorStore = opts.cursorStore ?? new MemoryCursorStore();
|
|
4964
4860
|
this._agentId = opts.agentId ?? "";
|
|
4965
4861
|
}
|
|
4966
4862
|
get agentId() {
|
|
@@ -5060,6 +4956,31 @@ var Client = class {
|
|
|
5060
4956
|
reason: input.reason
|
|
5061
4957
|
}).catch((err) => this.logger.warn("reportTaskFinished failed", err));
|
|
5062
4958
|
}
|
|
4959
|
+
reportActivity(input) {
|
|
4960
|
+
void this.t.perform({
|
|
4961
|
+
op: "report_activity",
|
|
4962
|
+
channelId: input.channelId,
|
|
4963
|
+
sequence: this.nextActivitySequence(),
|
|
4964
|
+
activity: input.activity
|
|
4965
|
+
}).catch((err) => this.logger.warn("reportActivity failed", err));
|
|
4966
|
+
}
|
|
4967
|
+
reportTurnActivity(input) {
|
|
4968
|
+
return new TurnActivityReporter({
|
|
4969
|
+
...input,
|
|
4970
|
+
deliver: (report) => this.reportActivity(report)
|
|
4971
|
+
});
|
|
4972
|
+
}
|
|
4973
|
+
/**
|
|
4974
|
+
* The counter every activity report carries. It belongs to the connection
|
|
4975
|
+
* rather than to a producer: both readers use it only to drop anything not
|
|
4976
|
+
* newer, so two producers on one agent numbering independently would silently
|
|
4977
|
+
* erase each other, and the failure would present as a missing report rather
|
|
4978
|
+
* than as a wrong one. Nothing outside this client can supply or observe it.
|
|
4979
|
+
*/
|
|
4980
|
+
nextActivitySequence() {
|
|
4981
|
+
this.activitySequence += 1;
|
|
4982
|
+
return this.activitySequence;
|
|
4983
|
+
}
|
|
5063
4984
|
// ─── Task CRUD (BPP semantic actions) ───────────────────
|
|
5064
4985
|
// Semantic actions rather than REST: the blueprint's protocol red line
|
|
5065
4986
|
// (plugin-protocol.md 1.3) is that a plugin does not reach past the semantic
|
|
@@ -5143,6 +5064,9 @@ var Client = class {
|
|
|
5143
5064
|
onServerRequest(handler) {
|
|
5144
5065
|
this.t.setServerRequestHandler?.(handler);
|
|
5145
5066
|
}
|
|
5067
|
+
onStopTurn(handler) {
|
|
5068
|
+
this.t.setStopTurnHandler?.(handler);
|
|
5069
|
+
}
|
|
5146
5070
|
emit(event, payload) {
|
|
5147
5071
|
for (const h of this.listeners[event])
|
|
5148
5072
|
h(payload);
|
|
@@ -5153,7 +5077,6 @@ var Client = class {
|
|
|
5153
5077
|
onConfigUpdate: (u) => this.emit("configUpdate", u),
|
|
5154
5078
|
applyConfigUpdate: this.opts.configUpdateHandler,
|
|
5155
5079
|
onPermissionDenied: (d) => this.emit("permissionDenied", d),
|
|
5156
|
-
onReplaySummary: (summary) => this.emit("replaySummary", summary),
|
|
5157
5080
|
onStateChange: (s) => {
|
|
5158
5081
|
this._state = s;
|
|
5159
5082
|
this.emit("connectionState", s);
|
|
@@ -5166,12 +5089,8 @@ var Client = class {
|
|
|
5166
5089
|
apiKey: this.opts.apiKey,
|
|
5167
5090
|
agentId: this._agentId,
|
|
5168
5091
|
pluginId: this.opts.pluginId,
|
|
5169
|
-
cursorStore: this.cursorStore,
|
|
5170
5092
|
logger: this.logger,
|
|
5171
|
-
|
|
5172
|
-
configApplyTimeoutMs: this.opts.configApplyTimeoutMs,
|
|
5173
|
-
replayMode: this.opts.replayMode,
|
|
5174
|
-
replayLatestN: this.opts.replayLatestN
|
|
5093
|
+
configApplyTimeoutMs: this.opts.configApplyTimeoutMs
|
|
5175
5094
|
};
|
|
5176
5095
|
}
|
|
5177
5096
|
};
|
|
@@ -5180,8 +5099,6 @@ function createBorgeePlugin(opts) {
|
|
|
5180
5099
|
}
|
|
5181
5100
|
export {
|
|
5182
5101
|
BorgeeError,
|
|
5183
|
-
FileCursorStore,
|
|
5184
|
-
MemoryCursorStore,
|
|
5185
5102
|
PermissionDeniedError,
|
|
5186
5103
|
createBorgeePlugin
|
|
5187
5104
|
};
|