@maudecode/openclaw-cowtail 0.12.3 → 0.13.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 +342 -11
- package/dist/setup-entry.js +342 -11
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -13874,6 +13874,20 @@ var openclawPluginMessageUpdateCommandSchema = requireOpenClawRenderableContent(
|
|
|
13874
13874
|
actions: exports_external.array(openclawActionDraftSchema).optional(),
|
|
13875
13875
|
deliveryState: openclawDeliveryStateSchema.optional()
|
|
13876
13876
|
}));
|
|
13877
|
+
var openclawMessageStreamSnapshotCommandBaseSchema = exports_external.object({
|
|
13878
|
+
type: exports_external.literal("openclaw_message_stream_snapshot"),
|
|
13879
|
+
requestId: openclawRequestIdSchema,
|
|
13880
|
+
streamId: nonEmptyStringSchema,
|
|
13881
|
+
sessionKey: nonEmptyStringSchema,
|
|
13882
|
+
threadId: nonEmptyStringSchema,
|
|
13883
|
+
text: openclawMessageTextSchema,
|
|
13884
|
+
links: exports_external.array(openclawLinkSchema).default([]),
|
|
13885
|
+
toolCalls: exports_external.array(openclawToolCallRecordSchema).default([]),
|
|
13886
|
+
isFinal: exports_external.boolean(),
|
|
13887
|
+
updatedAt: timestampSchema
|
|
13888
|
+
});
|
|
13889
|
+
var openclawMessageStreamSnapshotCommandSchema = requireOpenClawRenderableContent(openclawMessageStreamSnapshotCommandBaseSchema);
|
|
13890
|
+
var openclawMessageStreamSnapshotServerMessageSchema = requireOpenClawRenderableContent(openclawMessageStreamSnapshotCommandBaseSchema.omit({ requestId: true }));
|
|
13877
13891
|
var openclawIosNewThreadCommandSchema = exports_external.object({
|
|
13878
13892
|
type: exports_external.literal("ios_new_thread"),
|
|
13879
13893
|
requestId: openclawRequestIdSchema,
|
|
@@ -13932,6 +13946,7 @@ var openclawActionResultCommandSchema = exports_external.object({
|
|
|
13932
13946
|
var openclawRealtimeClientMessageSchema = exports_external.union([
|
|
13933
13947
|
openclawPluginMessageCommandSchema,
|
|
13934
13948
|
openclawPluginMessageUpdateCommandSchema,
|
|
13949
|
+
openclawMessageStreamSnapshotCommandSchema,
|
|
13935
13950
|
openclawIosNewThreadCommandSchema,
|
|
13936
13951
|
openclawIosReplyCommandSchema,
|
|
13937
13952
|
openclawIosActionCommandSchema,
|
|
@@ -13954,6 +13969,7 @@ var openclawRealtimeErrorSchema = exports_external.object({
|
|
|
13954
13969
|
});
|
|
13955
13970
|
var openclawRealtimeServerMessageSchema = exports_external.union([
|
|
13956
13971
|
openclawEventEnvelopeSchema,
|
|
13972
|
+
openclawMessageStreamSnapshotServerMessageSchema,
|
|
13957
13973
|
openclawRealtimeAckSchema,
|
|
13958
13974
|
openclawRealtimeErrorSchema
|
|
13959
13975
|
]);
|
|
@@ -14212,6 +14228,12 @@ class CowtailRealtimeClient {
|
|
|
14212
14228
|
idempotencyKey: command.idempotencyKey ?? `cowtail:request:${requestId}`
|
|
14213
14229
|
}).then((result) => result.sequence);
|
|
14214
14230
|
}
|
|
14231
|
+
sendOpenClawStreamSnapshot(command) {
|
|
14232
|
+
return this.#sendTransientCommand({
|
|
14233
|
+
...command,
|
|
14234
|
+
requestId: this.#requestIdFactory()
|
|
14235
|
+
});
|
|
14236
|
+
}
|
|
14215
14237
|
#connect() {
|
|
14216
14238
|
if (!this.#started || this.#socket) {
|
|
14217
14239
|
return;
|
|
@@ -14311,6 +14333,9 @@ class CowtailRealtimeClient {
|
|
|
14311
14333
|
this.#rejectPendingRequest(message);
|
|
14312
14334
|
return;
|
|
14313
14335
|
}
|
|
14336
|
+
if (message.type === "openclaw_message_stream_snapshot") {
|
|
14337
|
+
return;
|
|
14338
|
+
}
|
|
14314
14339
|
await this.#onEvent(message);
|
|
14315
14340
|
await this.#stateStore.writeLastSeenSequence(message.sequence);
|
|
14316
14341
|
}
|
|
@@ -14387,6 +14412,19 @@ class CowtailRealtimeClient {
|
|
|
14387
14412
|
}
|
|
14388
14413
|
});
|
|
14389
14414
|
}
|
|
14415
|
+
async#sendTransientCommand(command) {
|
|
14416
|
+
const socket = this.#socket;
|
|
14417
|
+
const handshake = this.#handshake;
|
|
14418
|
+
if (!socket || !handshake) {
|
|
14419
|
+
throw new Error("Cowtail websocket is disconnected");
|
|
14420
|
+
}
|
|
14421
|
+
await handshake.promise;
|
|
14422
|
+
const currentSocket = this.#socket;
|
|
14423
|
+
if (!currentSocket || socket !== currentSocket || handshake !== this.#handshake || !this.#isSocketOpen(currentSocket)) {
|
|
14424
|
+
throw new Error("Cowtail websocket is disconnected");
|
|
14425
|
+
}
|
|
14426
|
+
currentSocket.send(JSON.stringify(command));
|
|
14427
|
+
}
|
|
14390
14428
|
#resolvePendingRequest(requestId, sequence, payload) {
|
|
14391
14429
|
const pending = this.#pendingRequests.get(requestId);
|
|
14392
14430
|
if (!pending) {
|
|
@@ -14491,6 +14529,8 @@ var CHANNEL_LABEL = "Cowtail";
|
|
|
14491
14529
|
var SENDER_LABEL = "Cowtail iOS";
|
|
14492
14530
|
var SENDER_ID = "cowtail-ios";
|
|
14493
14531
|
var SENDER_ADDRESS = "cowtail:ios";
|
|
14532
|
+
var STREAM_MIN_INTERVAL_MS = 45;
|
|
14533
|
+
var STREAM_MIN_CHARS_DELTA = 3;
|
|
14494
14534
|
async function handleCowtailEvent(params) {
|
|
14495
14535
|
const { event, account, client, runtime, logger } = params;
|
|
14496
14536
|
switch (event.type) {
|
|
@@ -14626,7 +14666,10 @@ async function dispatchCowtailTextTurn(params) {
|
|
|
14626
14666
|
});
|
|
14627
14667
|
const streamState = {
|
|
14628
14668
|
idempotencyKey: `cowtail:reply:${message.id}`,
|
|
14669
|
+
streamId: `cowtail:stream:${message.id}`,
|
|
14629
14670
|
updateIndex: 0,
|
|
14671
|
+
toolFallbackIndex: 0,
|
|
14672
|
+
toolFallbackIds: {},
|
|
14630
14673
|
text: "",
|
|
14631
14674
|
links: [],
|
|
14632
14675
|
toolCalls: []
|
|
@@ -14636,12 +14679,17 @@ async function dispatchCowtailTextTurn(params) {
|
|
|
14636
14679
|
channel: CHANNEL_ID,
|
|
14637
14680
|
accountId: account.accountId,
|
|
14638
14681
|
agentId: route.agentId,
|
|
14682
|
+
client,
|
|
14683
|
+
logger,
|
|
14639
14684
|
route,
|
|
14685
|
+
thread,
|
|
14640
14686
|
storePath,
|
|
14641
14687
|
ctxPayload,
|
|
14642
14688
|
runtime,
|
|
14689
|
+
streamState,
|
|
14643
14690
|
deliver: async (payload, info, rawPayload) => deliverCowtailReply({
|
|
14644
14691
|
client,
|
|
14692
|
+
logger,
|
|
14645
14693
|
route,
|
|
14646
14694
|
thread,
|
|
14647
14695
|
payload,
|
|
@@ -14658,6 +14706,9 @@ async function dispatchCowtailTextTurn(params) {
|
|
|
14658
14706
|
});
|
|
14659
14707
|
await finalizeCowtailStreamedReply({
|
|
14660
14708
|
client,
|
|
14709
|
+
logger,
|
|
14710
|
+
route,
|
|
14711
|
+
thread,
|
|
14661
14712
|
streamState
|
|
14662
14713
|
});
|
|
14663
14714
|
}
|
|
@@ -14688,7 +14739,10 @@ async function dispatchCowtailActionTurn(params) {
|
|
|
14688
14739
|
let dispatchFailed = false;
|
|
14689
14740
|
const streamState = {
|
|
14690
14741
|
idempotencyKey: `cowtail:action:${action.id}`,
|
|
14742
|
+
streamId: `cowtail:action-stream:${action.id}`,
|
|
14691
14743
|
updateIndex: 0,
|
|
14744
|
+
toolFallbackIndex: 0,
|
|
14745
|
+
toolFallbackIds: {},
|
|
14692
14746
|
text: "",
|
|
14693
14747
|
links: [],
|
|
14694
14748
|
toolCalls: []
|
|
@@ -14698,12 +14752,17 @@ async function dispatchCowtailActionTurn(params) {
|
|
|
14698
14752
|
channel: CHANNEL_ID,
|
|
14699
14753
|
accountId: account.accountId,
|
|
14700
14754
|
agentId: route.agentId,
|
|
14755
|
+
client,
|
|
14756
|
+
logger,
|
|
14701
14757
|
route,
|
|
14758
|
+
thread,
|
|
14702
14759
|
storePath,
|
|
14703
14760
|
ctxPayload,
|
|
14704
14761
|
runtime,
|
|
14762
|
+
streamState,
|
|
14705
14763
|
deliver: async (replyPayload, info, rawPayload) => deliverCowtailReply({
|
|
14706
14764
|
client,
|
|
14765
|
+
logger,
|
|
14707
14766
|
route,
|
|
14708
14767
|
thread,
|
|
14709
14768
|
payload: replyPayload,
|
|
@@ -14721,6 +14780,9 @@ async function dispatchCowtailActionTurn(params) {
|
|
|
14721
14780
|
});
|
|
14722
14781
|
await finalizeCowtailStreamedReply({
|
|
14723
14782
|
client,
|
|
14783
|
+
logger,
|
|
14784
|
+
route,
|
|
14785
|
+
thread,
|
|
14724
14786
|
streamState
|
|
14725
14787
|
});
|
|
14726
14788
|
return !dispatchFailed;
|
|
@@ -14754,7 +14816,79 @@ async function recordCowtailInboundSessionAndDispatchReply(params) {
|
|
|
14754
14816
|
},
|
|
14755
14817
|
replyOptions: {
|
|
14756
14818
|
disableBlockStreaming: false,
|
|
14757
|
-
onModelSelected
|
|
14819
|
+
onModelSelected,
|
|
14820
|
+
onPartialReply: (payload) => {
|
|
14821
|
+
deliverCowtailPartialReply({
|
|
14822
|
+
client: params.client,
|
|
14823
|
+
logger: params.logger,
|
|
14824
|
+
route: params.route,
|
|
14825
|
+
thread: params.thread,
|
|
14826
|
+
payload: normalizeCowtailReplyPayload(payload),
|
|
14827
|
+
streamState: params.streamState
|
|
14828
|
+
});
|
|
14829
|
+
},
|
|
14830
|
+
onToolStart: (payload) => {
|
|
14831
|
+
deliverCowtailToolProgress({
|
|
14832
|
+
client: params.client,
|
|
14833
|
+
logger: params.logger,
|
|
14834
|
+
route: params.route,
|
|
14835
|
+
thread: params.thread,
|
|
14836
|
+
streamState: params.streamState,
|
|
14837
|
+
payload,
|
|
14838
|
+
requireStableId: true
|
|
14839
|
+
});
|
|
14840
|
+
},
|
|
14841
|
+
onItemEvent: (payload) => {
|
|
14842
|
+
deliverCowtailToolProgress({
|
|
14843
|
+
client: params.client,
|
|
14844
|
+
logger: params.logger,
|
|
14845
|
+
route: params.route,
|
|
14846
|
+
thread: params.thread,
|
|
14847
|
+
streamState: params.streamState,
|
|
14848
|
+
payload
|
|
14849
|
+
});
|
|
14850
|
+
},
|
|
14851
|
+
onCommandOutput: (payload) => {
|
|
14852
|
+
deliverCowtailToolProgress({
|
|
14853
|
+
client: params.client,
|
|
14854
|
+
logger: params.logger,
|
|
14855
|
+
route: params.route,
|
|
14856
|
+
thread: params.thread,
|
|
14857
|
+
streamState: params.streamState,
|
|
14858
|
+
payload
|
|
14859
|
+
});
|
|
14860
|
+
},
|
|
14861
|
+
onPatchSummary: (payload) => {
|
|
14862
|
+
deliverCowtailToolProgress({
|
|
14863
|
+
client: params.client,
|
|
14864
|
+
logger: params.logger,
|
|
14865
|
+
route: params.route,
|
|
14866
|
+
thread: params.thread,
|
|
14867
|
+
streamState: params.streamState,
|
|
14868
|
+
payload
|
|
14869
|
+
});
|
|
14870
|
+
},
|
|
14871
|
+
onApprovalEvent: (payload) => {
|
|
14872
|
+
deliverCowtailToolProgress({
|
|
14873
|
+
client: params.client,
|
|
14874
|
+
logger: params.logger,
|
|
14875
|
+
route: params.route,
|
|
14876
|
+
thread: params.thread,
|
|
14877
|
+
streamState: params.streamState,
|
|
14878
|
+
payload
|
|
14879
|
+
});
|
|
14880
|
+
},
|
|
14881
|
+
onToolResult: (payload) => {
|
|
14882
|
+
deliverCowtailToolProgress({
|
|
14883
|
+
client: params.client,
|
|
14884
|
+
logger: params.logger,
|
|
14885
|
+
route: params.route,
|
|
14886
|
+
thread: params.thread,
|
|
14887
|
+
streamState: params.streamState,
|
|
14888
|
+
payload,
|
|
14889
|
+
status: "complete"
|
|
14890
|
+
});
|
|
14891
|
+
}
|
|
14758
14892
|
}
|
|
14759
14893
|
});
|
|
14760
14894
|
}
|
|
@@ -14774,10 +14908,20 @@ async function deliverCowtailReply(params) {
|
|
|
14774
14908
|
rawPayload: params.rawPayload,
|
|
14775
14909
|
streamState: params.streamState
|
|
14776
14910
|
});
|
|
14777
|
-
params.streamState
|
|
14911
|
+
upsertCowtailToolCall(params.streamState, toolCall);
|
|
14778
14912
|
const messageText = params.streamState.text;
|
|
14913
|
+
emitCowtailStreamSnapshot({
|
|
14914
|
+
client: params.client,
|
|
14915
|
+
logger: params.logger,
|
|
14916
|
+
route: params.route,
|
|
14917
|
+
thread: params.thread,
|
|
14918
|
+
streamState: params.streamState,
|
|
14919
|
+
text: currentLiveCowtailStreamText(params.streamState),
|
|
14920
|
+
isFinal: false,
|
|
14921
|
+
updatedAt: Date.now()
|
|
14922
|
+
});
|
|
14779
14923
|
if (!params.streamState.messageId) {
|
|
14780
|
-
const
|
|
14924
|
+
const result2 = await params.client.sendOpenClawMessage({
|
|
14781
14925
|
type: "openclaw_message",
|
|
14782
14926
|
idempotencyKey: params.streamState.idempotencyKey,
|
|
14783
14927
|
sessionKey: params.thread.sessionKey ?? params.route.sessionKey,
|
|
@@ -14789,11 +14933,11 @@ async function deliverCowtailReply(params) {
|
|
|
14789
14933
|
actions: [],
|
|
14790
14934
|
deliveryState: "pending"
|
|
14791
14935
|
});
|
|
14792
|
-
const
|
|
14793
|
-
if (!
|
|
14936
|
+
const messageId2 = recordCreatedOpenClawMessageId(params.streamState, result2);
|
|
14937
|
+
if (!messageId2) {
|
|
14794
14938
|
return;
|
|
14795
14939
|
}
|
|
14796
|
-
params.streamState.messageId =
|
|
14940
|
+
params.streamState.messageId = messageId2;
|
|
14797
14941
|
return;
|
|
14798
14942
|
}
|
|
14799
14943
|
await params.client.sendOpenClawMessageUpdate({
|
|
@@ -14812,8 +14956,18 @@ async function deliverCowtailReply(params) {
|
|
|
14812
14956
|
}
|
|
14813
14957
|
if (kind === "block") {
|
|
14814
14958
|
params.streamState.text = appendReplyBlock(params.streamState.text, text);
|
|
14959
|
+
emitCowtailStreamSnapshot({
|
|
14960
|
+
client: params.client,
|
|
14961
|
+
logger: params.logger,
|
|
14962
|
+
route: params.route,
|
|
14963
|
+
thread: params.thread,
|
|
14964
|
+
streamState: params.streamState,
|
|
14965
|
+
text: params.streamState.text,
|
|
14966
|
+
isFinal: false,
|
|
14967
|
+
updatedAt: Date.now()
|
|
14968
|
+
});
|
|
14815
14969
|
if (!params.streamState.messageId) {
|
|
14816
|
-
const
|
|
14970
|
+
const result2 = await params.client.sendOpenClawMessage({
|
|
14817
14971
|
type: "openclaw_message",
|
|
14818
14972
|
idempotencyKey: params.streamState.idempotencyKey,
|
|
14819
14973
|
sessionKey: params.thread.sessionKey ?? params.route.sessionKey,
|
|
@@ -14825,11 +14979,11 @@ async function deliverCowtailReply(params) {
|
|
|
14825
14979
|
actions: [],
|
|
14826
14980
|
deliveryState: "pending"
|
|
14827
14981
|
});
|
|
14828
|
-
const
|
|
14829
|
-
if (!
|
|
14982
|
+
const messageId2 = recordCreatedOpenClawMessageId(params.streamState, result2);
|
|
14983
|
+
if (!messageId2) {
|
|
14830
14984
|
return;
|
|
14831
14985
|
}
|
|
14832
|
-
params.streamState.messageId =
|
|
14986
|
+
params.streamState.messageId = messageId2;
|
|
14833
14987
|
return;
|
|
14834
14988
|
}
|
|
14835
14989
|
await params.client.sendOpenClawMessageUpdate({
|
|
@@ -14855,10 +15009,21 @@ async function deliverCowtailReply(params) {
|
|
|
14855
15009
|
actions: [],
|
|
14856
15010
|
deliveryState: "sent"
|
|
14857
15011
|
});
|
|
15012
|
+
emitCowtailStreamSnapshot({
|
|
15013
|
+
client: params.client,
|
|
15014
|
+
logger: params.logger,
|
|
15015
|
+
route: params.route,
|
|
15016
|
+
thread: params.thread,
|
|
15017
|
+
streamState: params.streamState,
|
|
15018
|
+
text,
|
|
15019
|
+
isFinal: true,
|
|
15020
|
+
updatedAt: Date.now()
|
|
15021
|
+
});
|
|
14858
15022
|
params.streamState.completed = true;
|
|
14859
15023
|
return;
|
|
14860
15024
|
}
|
|
14861
|
-
|
|
15025
|
+
params.streamState.text = text;
|
|
15026
|
+
const result = await params.client.sendOpenClawMessage({
|
|
14862
15027
|
type: "openclaw_message",
|
|
14863
15028
|
idempotencyKey: params.streamState.idempotencyKey,
|
|
14864
15029
|
sessionKey: params.thread.sessionKey ?? params.route.sessionKey,
|
|
@@ -14870,6 +15035,21 @@ async function deliverCowtailReply(params) {
|
|
|
14870
15035
|
actions: [],
|
|
14871
15036
|
deliveryState: "sent"
|
|
14872
15037
|
});
|
|
15038
|
+
const messageId = recordCreatedOpenClawMessageId(params.streamState, result);
|
|
15039
|
+
if (!messageId) {
|
|
15040
|
+
return;
|
|
15041
|
+
}
|
|
15042
|
+
params.streamState.messageId = messageId;
|
|
15043
|
+
emitCowtailStreamSnapshot({
|
|
15044
|
+
client: params.client,
|
|
15045
|
+
logger: params.logger,
|
|
15046
|
+
route: params.route,
|
|
15047
|
+
thread: params.thread,
|
|
15048
|
+
streamState: params.streamState,
|
|
15049
|
+
text,
|
|
15050
|
+
isFinal: true,
|
|
15051
|
+
updatedAt: Date.now()
|
|
15052
|
+
});
|
|
14873
15053
|
params.streamState.completed = true;
|
|
14874
15054
|
}
|
|
14875
15055
|
async function finalizeCowtailStreamedReply(params) {
|
|
@@ -14886,8 +15066,117 @@ async function finalizeCowtailStreamedReply(params) {
|
|
|
14886
15066
|
actions: [],
|
|
14887
15067
|
deliveryState: "sent"
|
|
14888
15068
|
});
|
|
15069
|
+
emitCowtailStreamSnapshot({
|
|
15070
|
+
client: params.client,
|
|
15071
|
+
logger: params.logger,
|
|
15072
|
+
route: params.route,
|
|
15073
|
+
thread: params.thread,
|
|
15074
|
+
streamState: params.streamState,
|
|
15075
|
+
text: params.streamState.text,
|
|
15076
|
+
isFinal: true,
|
|
15077
|
+
updatedAt: Date.now()
|
|
15078
|
+
});
|
|
14889
15079
|
params.streamState.completed = true;
|
|
14890
15080
|
}
|
|
15081
|
+
function deliverCowtailPartialReply(params) {
|
|
15082
|
+
const text = params.payload.text;
|
|
15083
|
+
if (params.streamState.failed || typeof text !== "string" || !text.trim()) {
|
|
15084
|
+
return;
|
|
15085
|
+
}
|
|
15086
|
+
const now = Date.now();
|
|
15087
|
+
const previous = params.streamState.lastSnapshotText ?? "";
|
|
15088
|
+
const changedChars = Math.abs(text.length - previous.length);
|
|
15089
|
+
const sentRecently = params.streamState.lastSnapshotSentAt !== undefined && now - params.streamState.lastSnapshotSentAt < STREAM_MIN_INTERVAL_MS;
|
|
15090
|
+
if (text === previous || sentRecently && changedChars < STREAM_MIN_CHARS_DELTA) {
|
|
15091
|
+
return;
|
|
15092
|
+
}
|
|
15093
|
+
emitCowtailStreamSnapshot({
|
|
15094
|
+
client: params.client,
|
|
15095
|
+
logger: params.logger,
|
|
15096
|
+
route: params.route,
|
|
15097
|
+
thread: params.thread,
|
|
15098
|
+
streamState: params.streamState,
|
|
15099
|
+
text,
|
|
15100
|
+
isFinal: false,
|
|
15101
|
+
updatedAt: now
|
|
15102
|
+
});
|
|
15103
|
+
}
|
|
15104
|
+
function deliverCowtailToolProgress(params) {
|
|
15105
|
+
if (params.streamState.failed) {
|
|
15106
|
+
return;
|
|
15107
|
+
}
|
|
15108
|
+
const payload = readRecord(params.payload) ?? {};
|
|
15109
|
+
const stableId = readString(payload.itemId) ?? readString(payload.toolCallId) ?? readString(payload.id);
|
|
15110
|
+
if (params.requireStableId && !stableId) {
|
|
15111
|
+
return;
|
|
15112
|
+
}
|
|
15113
|
+
const id = stableId ?? fallbackCowtailToolCallId(params.streamState, payload);
|
|
15114
|
+
const name = readString(payload.name) ?? readString(payload.title) ?? readString(payload.command) ?? "tool_result";
|
|
15115
|
+
const status = params.status ?? normalizeCowtailToolCallStatus(payload.status) ?? normalizeCowtailToolCallStatus(payload.phase) ?? "running";
|
|
15116
|
+
const result = readCowtailToolProgressResult(payload);
|
|
15117
|
+
const args = readRecord(payload.args) ?? readRecord(payload.input);
|
|
15118
|
+
const now = Date.now();
|
|
15119
|
+
const currentText = currentLiveCowtailStreamText(params.streamState);
|
|
15120
|
+
const nextToolCall = {
|
|
15121
|
+
id,
|
|
15122
|
+
name,
|
|
15123
|
+
...args ? { args } : {},
|
|
15124
|
+
...result !== undefined ? { result } : {},
|
|
15125
|
+
status,
|
|
15126
|
+
...status === "complete" || status === "error" ? { completedAt: now } : {},
|
|
15127
|
+
insertedAtContentLength: currentText.length,
|
|
15128
|
+
contentSnapshotAtStart: currentText
|
|
15129
|
+
};
|
|
15130
|
+
upsertCowtailToolCall(params.streamState, nextToolCall);
|
|
15131
|
+
emitCowtailStreamSnapshot({
|
|
15132
|
+
client: params.client,
|
|
15133
|
+
logger: params.logger,
|
|
15134
|
+
route: params.route,
|
|
15135
|
+
thread: params.thread,
|
|
15136
|
+
streamState: params.streamState,
|
|
15137
|
+
text: currentText,
|
|
15138
|
+
isFinal: false,
|
|
15139
|
+
updatedAt: now
|
|
15140
|
+
});
|
|
15141
|
+
}
|
|
15142
|
+
function emitCowtailStreamSnapshot(params) {
|
|
15143
|
+
if (params.streamState.failed || !params.text.trim() && params.streamState.links.length === 0 && params.streamState.toolCalls.length === 0) {
|
|
15144
|
+
return;
|
|
15145
|
+
}
|
|
15146
|
+
params.streamState.liveText = params.text;
|
|
15147
|
+
params.streamState.lastSnapshotText = params.text;
|
|
15148
|
+
params.streamState.lastSnapshotSentAt = params.updatedAt;
|
|
15149
|
+
params.client.sendOpenClawStreamSnapshot({
|
|
15150
|
+
type: "openclaw_message_stream_snapshot",
|
|
15151
|
+
streamId: params.streamState.streamId,
|
|
15152
|
+
sessionKey: params.thread.sessionKey ?? params.route.sessionKey,
|
|
15153
|
+
threadId: params.thread.id,
|
|
15154
|
+
text: params.text,
|
|
15155
|
+
links: params.streamState.links.map((link) => ({ ...link })),
|
|
15156
|
+
toolCalls: structuredClone(params.streamState.toolCalls),
|
|
15157
|
+
isFinal: params.isFinal,
|
|
15158
|
+
updatedAt: params.updatedAt
|
|
15159
|
+
}).catch((error48) => {
|
|
15160
|
+
params.logger?.warn?.(`Cowtail stream snapshot send failed: ${errorMessage(error48)}`);
|
|
15161
|
+
});
|
|
15162
|
+
}
|
|
15163
|
+
function currentLiveCowtailStreamText(streamState) {
|
|
15164
|
+
return streamState.liveText ?? streamState.text;
|
|
15165
|
+
}
|
|
15166
|
+
function upsertCowtailToolCall(streamState, toolCall) {
|
|
15167
|
+
const index = streamState.toolCalls.findIndex((candidate) => candidate.id === toolCall.id);
|
|
15168
|
+
if (index < 0) {
|
|
15169
|
+
streamState.toolCalls = [...streamState.toolCalls, toolCall];
|
|
15170
|
+
return;
|
|
15171
|
+
}
|
|
15172
|
+
const existing = streamState.toolCalls[index];
|
|
15173
|
+
streamState.toolCalls[index] = {
|
|
15174
|
+
...existing,
|
|
15175
|
+
...toolCall,
|
|
15176
|
+
insertedAtContentLength: existing.insertedAtContentLength ?? toolCall.insertedAtContentLength,
|
|
15177
|
+
contentSnapshotAtStart: existing.contentSnapshotAtStart ?? toolCall.contentSnapshotAtStart
|
|
15178
|
+
};
|
|
15179
|
+
}
|
|
14891
15180
|
function recordCreatedOpenClawMessageId(streamState, result) {
|
|
14892
15181
|
if (result.payload?.dropped === true) {
|
|
14893
15182
|
if (result.payload.duplicate === true) {
|
|
@@ -14983,6 +15272,48 @@ function readRecord(value) {
|
|
|
14983
15272
|
function readToolCallStatus(value) {
|
|
14984
15273
|
return value === "pending" || value === "running" || value === "complete" || value === "error" ? value : undefined;
|
|
14985
15274
|
}
|
|
15275
|
+
function normalizeCowtailToolCallStatus(value) {
|
|
15276
|
+
const status = readToolCallStatus(value);
|
|
15277
|
+
if (status) {
|
|
15278
|
+
return status;
|
|
15279
|
+
}
|
|
15280
|
+
if (value === "start" || value === "started" || value === "working") {
|
|
15281
|
+
return "running";
|
|
15282
|
+
}
|
|
15283
|
+
if (value === "end" || value === "done" || value === "completed" || value === "success") {
|
|
15284
|
+
return "complete";
|
|
15285
|
+
}
|
|
15286
|
+
if (value === "failed" || value === "failure") {
|
|
15287
|
+
return "error";
|
|
15288
|
+
}
|
|
15289
|
+
return;
|
|
15290
|
+
}
|
|
15291
|
+
function readCowtailToolProgressResult(payload) {
|
|
15292
|
+
const stringResult = readString(payload.output) ?? readString(payload.summary) ?? readString(payload.message) ?? readString(payload.progressText) ?? readString(payload.text);
|
|
15293
|
+
if (stringResult !== undefined) {
|
|
15294
|
+
return stringResult;
|
|
15295
|
+
}
|
|
15296
|
+
if (payload.result !== undefined) {
|
|
15297
|
+
return payload.result;
|
|
15298
|
+
}
|
|
15299
|
+
const structuredPatch = {
|
|
15300
|
+
...Array.isArray(payload.added) ? { added: payload.added } : {},
|
|
15301
|
+
...Array.isArray(payload.modified) ? { modified: payload.modified } : {},
|
|
15302
|
+
...Array.isArray(payload.deleted) ? { deleted: payload.deleted } : {}
|
|
15303
|
+
};
|
|
15304
|
+
return Object.keys(structuredPatch).length > 0 ? structuredPatch : undefined;
|
|
15305
|
+
}
|
|
15306
|
+
function fallbackCowtailToolCallId(streamState, payload) {
|
|
15307
|
+
const key = readString(payload.name) ?? readString(payload.title) ?? readString(payload.command) ?? JSON.stringify(Object.keys(payload).sort());
|
|
15308
|
+
const existing = streamState.toolFallbackIds[key];
|
|
15309
|
+
if (existing) {
|
|
15310
|
+
return existing;
|
|
15311
|
+
}
|
|
15312
|
+
streamState.toolFallbackIndex += 1;
|
|
15313
|
+
const next = `tool-${streamState.toolFallbackIndex}`;
|
|
15314
|
+
streamState.toolFallbackIds[key] = next;
|
|
15315
|
+
return next;
|
|
15316
|
+
}
|
|
14986
15317
|
function readTimestamp(value) {
|
|
14987
15318
|
return typeof value === "number" && Number.isInteger(value) && value >= 0 ? value : undefined;
|
|
14988
15319
|
}
|
package/dist/setup-entry.js
CHANGED
|
@@ -13874,6 +13874,20 @@ var openclawPluginMessageUpdateCommandSchema = requireOpenClawRenderableContent(
|
|
|
13874
13874
|
actions: exports_external.array(openclawActionDraftSchema).optional(),
|
|
13875
13875
|
deliveryState: openclawDeliveryStateSchema.optional()
|
|
13876
13876
|
}));
|
|
13877
|
+
var openclawMessageStreamSnapshotCommandBaseSchema = exports_external.object({
|
|
13878
|
+
type: exports_external.literal("openclaw_message_stream_snapshot"),
|
|
13879
|
+
requestId: openclawRequestIdSchema,
|
|
13880
|
+
streamId: nonEmptyStringSchema,
|
|
13881
|
+
sessionKey: nonEmptyStringSchema,
|
|
13882
|
+
threadId: nonEmptyStringSchema,
|
|
13883
|
+
text: openclawMessageTextSchema,
|
|
13884
|
+
links: exports_external.array(openclawLinkSchema).default([]),
|
|
13885
|
+
toolCalls: exports_external.array(openclawToolCallRecordSchema).default([]),
|
|
13886
|
+
isFinal: exports_external.boolean(),
|
|
13887
|
+
updatedAt: timestampSchema
|
|
13888
|
+
});
|
|
13889
|
+
var openclawMessageStreamSnapshotCommandSchema = requireOpenClawRenderableContent(openclawMessageStreamSnapshotCommandBaseSchema);
|
|
13890
|
+
var openclawMessageStreamSnapshotServerMessageSchema = requireOpenClawRenderableContent(openclawMessageStreamSnapshotCommandBaseSchema.omit({ requestId: true }));
|
|
13877
13891
|
var openclawIosNewThreadCommandSchema = exports_external.object({
|
|
13878
13892
|
type: exports_external.literal("ios_new_thread"),
|
|
13879
13893
|
requestId: openclawRequestIdSchema,
|
|
@@ -13932,6 +13946,7 @@ var openclawActionResultCommandSchema = exports_external.object({
|
|
|
13932
13946
|
var openclawRealtimeClientMessageSchema = exports_external.union([
|
|
13933
13947
|
openclawPluginMessageCommandSchema,
|
|
13934
13948
|
openclawPluginMessageUpdateCommandSchema,
|
|
13949
|
+
openclawMessageStreamSnapshotCommandSchema,
|
|
13935
13950
|
openclawIosNewThreadCommandSchema,
|
|
13936
13951
|
openclawIosReplyCommandSchema,
|
|
13937
13952
|
openclawIosActionCommandSchema,
|
|
@@ -13954,6 +13969,7 @@ var openclawRealtimeErrorSchema = exports_external.object({
|
|
|
13954
13969
|
});
|
|
13955
13970
|
var openclawRealtimeServerMessageSchema = exports_external.union([
|
|
13956
13971
|
openclawEventEnvelopeSchema,
|
|
13972
|
+
openclawMessageStreamSnapshotServerMessageSchema,
|
|
13957
13973
|
openclawRealtimeAckSchema,
|
|
13958
13974
|
openclawRealtimeErrorSchema
|
|
13959
13975
|
]);
|
|
@@ -14212,6 +14228,12 @@ class CowtailRealtimeClient {
|
|
|
14212
14228
|
idempotencyKey: command.idempotencyKey ?? `cowtail:request:${requestId}`
|
|
14213
14229
|
}).then((result) => result.sequence);
|
|
14214
14230
|
}
|
|
14231
|
+
sendOpenClawStreamSnapshot(command) {
|
|
14232
|
+
return this.#sendTransientCommand({
|
|
14233
|
+
...command,
|
|
14234
|
+
requestId: this.#requestIdFactory()
|
|
14235
|
+
});
|
|
14236
|
+
}
|
|
14215
14237
|
#connect() {
|
|
14216
14238
|
if (!this.#started || this.#socket) {
|
|
14217
14239
|
return;
|
|
@@ -14311,6 +14333,9 @@ class CowtailRealtimeClient {
|
|
|
14311
14333
|
this.#rejectPendingRequest(message);
|
|
14312
14334
|
return;
|
|
14313
14335
|
}
|
|
14336
|
+
if (message.type === "openclaw_message_stream_snapshot") {
|
|
14337
|
+
return;
|
|
14338
|
+
}
|
|
14314
14339
|
await this.#onEvent(message);
|
|
14315
14340
|
await this.#stateStore.writeLastSeenSequence(message.sequence);
|
|
14316
14341
|
}
|
|
@@ -14387,6 +14412,19 @@ class CowtailRealtimeClient {
|
|
|
14387
14412
|
}
|
|
14388
14413
|
});
|
|
14389
14414
|
}
|
|
14415
|
+
async#sendTransientCommand(command) {
|
|
14416
|
+
const socket = this.#socket;
|
|
14417
|
+
const handshake = this.#handshake;
|
|
14418
|
+
if (!socket || !handshake) {
|
|
14419
|
+
throw new Error("Cowtail websocket is disconnected");
|
|
14420
|
+
}
|
|
14421
|
+
await handshake.promise;
|
|
14422
|
+
const currentSocket = this.#socket;
|
|
14423
|
+
if (!currentSocket || socket !== currentSocket || handshake !== this.#handshake || !this.#isSocketOpen(currentSocket)) {
|
|
14424
|
+
throw new Error("Cowtail websocket is disconnected");
|
|
14425
|
+
}
|
|
14426
|
+
currentSocket.send(JSON.stringify(command));
|
|
14427
|
+
}
|
|
14390
14428
|
#resolvePendingRequest(requestId, sequence, payload) {
|
|
14391
14429
|
const pending = this.#pendingRequests.get(requestId);
|
|
14392
14430
|
if (!pending) {
|
|
@@ -14491,6 +14529,8 @@ var CHANNEL_LABEL = "Cowtail";
|
|
|
14491
14529
|
var SENDER_LABEL = "Cowtail iOS";
|
|
14492
14530
|
var SENDER_ID = "cowtail-ios";
|
|
14493
14531
|
var SENDER_ADDRESS = "cowtail:ios";
|
|
14532
|
+
var STREAM_MIN_INTERVAL_MS = 45;
|
|
14533
|
+
var STREAM_MIN_CHARS_DELTA = 3;
|
|
14494
14534
|
async function handleCowtailEvent(params) {
|
|
14495
14535
|
const { event, account, client, runtime, logger } = params;
|
|
14496
14536
|
switch (event.type) {
|
|
@@ -14626,7 +14666,10 @@ async function dispatchCowtailTextTurn(params) {
|
|
|
14626
14666
|
});
|
|
14627
14667
|
const streamState = {
|
|
14628
14668
|
idempotencyKey: `cowtail:reply:${message.id}`,
|
|
14669
|
+
streamId: `cowtail:stream:${message.id}`,
|
|
14629
14670
|
updateIndex: 0,
|
|
14671
|
+
toolFallbackIndex: 0,
|
|
14672
|
+
toolFallbackIds: {},
|
|
14630
14673
|
text: "",
|
|
14631
14674
|
links: [],
|
|
14632
14675
|
toolCalls: []
|
|
@@ -14636,12 +14679,17 @@ async function dispatchCowtailTextTurn(params) {
|
|
|
14636
14679
|
channel: CHANNEL_ID,
|
|
14637
14680
|
accountId: account.accountId,
|
|
14638
14681
|
agentId: route.agentId,
|
|
14682
|
+
client,
|
|
14683
|
+
logger,
|
|
14639
14684
|
route,
|
|
14685
|
+
thread,
|
|
14640
14686
|
storePath,
|
|
14641
14687
|
ctxPayload,
|
|
14642
14688
|
runtime,
|
|
14689
|
+
streamState,
|
|
14643
14690
|
deliver: async (payload, info, rawPayload) => deliverCowtailReply({
|
|
14644
14691
|
client,
|
|
14692
|
+
logger,
|
|
14645
14693
|
route,
|
|
14646
14694
|
thread,
|
|
14647
14695
|
payload,
|
|
@@ -14658,6 +14706,9 @@ async function dispatchCowtailTextTurn(params) {
|
|
|
14658
14706
|
});
|
|
14659
14707
|
await finalizeCowtailStreamedReply({
|
|
14660
14708
|
client,
|
|
14709
|
+
logger,
|
|
14710
|
+
route,
|
|
14711
|
+
thread,
|
|
14661
14712
|
streamState
|
|
14662
14713
|
});
|
|
14663
14714
|
}
|
|
@@ -14688,7 +14739,10 @@ async function dispatchCowtailActionTurn(params) {
|
|
|
14688
14739
|
let dispatchFailed = false;
|
|
14689
14740
|
const streamState = {
|
|
14690
14741
|
idempotencyKey: `cowtail:action:${action.id}`,
|
|
14742
|
+
streamId: `cowtail:action-stream:${action.id}`,
|
|
14691
14743
|
updateIndex: 0,
|
|
14744
|
+
toolFallbackIndex: 0,
|
|
14745
|
+
toolFallbackIds: {},
|
|
14692
14746
|
text: "",
|
|
14693
14747
|
links: [],
|
|
14694
14748
|
toolCalls: []
|
|
@@ -14698,12 +14752,17 @@ async function dispatchCowtailActionTurn(params) {
|
|
|
14698
14752
|
channel: CHANNEL_ID,
|
|
14699
14753
|
accountId: account.accountId,
|
|
14700
14754
|
agentId: route.agentId,
|
|
14755
|
+
client,
|
|
14756
|
+
logger,
|
|
14701
14757
|
route,
|
|
14758
|
+
thread,
|
|
14702
14759
|
storePath,
|
|
14703
14760
|
ctxPayload,
|
|
14704
14761
|
runtime,
|
|
14762
|
+
streamState,
|
|
14705
14763
|
deliver: async (replyPayload, info, rawPayload) => deliverCowtailReply({
|
|
14706
14764
|
client,
|
|
14765
|
+
logger,
|
|
14707
14766
|
route,
|
|
14708
14767
|
thread,
|
|
14709
14768
|
payload: replyPayload,
|
|
@@ -14721,6 +14780,9 @@ async function dispatchCowtailActionTurn(params) {
|
|
|
14721
14780
|
});
|
|
14722
14781
|
await finalizeCowtailStreamedReply({
|
|
14723
14782
|
client,
|
|
14783
|
+
logger,
|
|
14784
|
+
route,
|
|
14785
|
+
thread,
|
|
14724
14786
|
streamState
|
|
14725
14787
|
});
|
|
14726
14788
|
return !dispatchFailed;
|
|
@@ -14754,7 +14816,79 @@ async function recordCowtailInboundSessionAndDispatchReply(params) {
|
|
|
14754
14816
|
},
|
|
14755
14817
|
replyOptions: {
|
|
14756
14818
|
disableBlockStreaming: false,
|
|
14757
|
-
onModelSelected
|
|
14819
|
+
onModelSelected,
|
|
14820
|
+
onPartialReply: (payload) => {
|
|
14821
|
+
deliverCowtailPartialReply({
|
|
14822
|
+
client: params.client,
|
|
14823
|
+
logger: params.logger,
|
|
14824
|
+
route: params.route,
|
|
14825
|
+
thread: params.thread,
|
|
14826
|
+
payload: normalizeCowtailReplyPayload(payload),
|
|
14827
|
+
streamState: params.streamState
|
|
14828
|
+
});
|
|
14829
|
+
},
|
|
14830
|
+
onToolStart: (payload) => {
|
|
14831
|
+
deliverCowtailToolProgress({
|
|
14832
|
+
client: params.client,
|
|
14833
|
+
logger: params.logger,
|
|
14834
|
+
route: params.route,
|
|
14835
|
+
thread: params.thread,
|
|
14836
|
+
streamState: params.streamState,
|
|
14837
|
+
payload,
|
|
14838
|
+
requireStableId: true
|
|
14839
|
+
});
|
|
14840
|
+
},
|
|
14841
|
+
onItemEvent: (payload) => {
|
|
14842
|
+
deliverCowtailToolProgress({
|
|
14843
|
+
client: params.client,
|
|
14844
|
+
logger: params.logger,
|
|
14845
|
+
route: params.route,
|
|
14846
|
+
thread: params.thread,
|
|
14847
|
+
streamState: params.streamState,
|
|
14848
|
+
payload
|
|
14849
|
+
});
|
|
14850
|
+
},
|
|
14851
|
+
onCommandOutput: (payload) => {
|
|
14852
|
+
deliverCowtailToolProgress({
|
|
14853
|
+
client: params.client,
|
|
14854
|
+
logger: params.logger,
|
|
14855
|
+
route: params.route,
|
|
14856
|
+
thread: params.thread,
|
|
14857
|
+
streamState: params.streamState,
|
|
14858
|
+
payload
|
|
14859
|
+
});
|
|
14860
|
+
},
|
|
14861
|
+
onPatchSummary: (payload) => {
|
|
14862
|
+
deliverCowtailToolProgress({
|
|
14863
|
+
client: params.client,
|
|
14864
|
+
logger: params.logger,
|
|
14865
|
+
route: params.route,
|
|
14866
|
+
thread: params.thread,
|
|
14867
|
+
streamState: params.streamState,
|
|
14868
|
+
payload
|
|
14869
|
+
});
|
|
14870
|
+
},
|
|
14871
|
+
onApprovalEvent: (payload) => {
|
|
14872
|
+
deliverCowtailToolProgress({
|
|
14873
|
+
client: params.client,
|
|
14874
|
+
logger: params.logger,
|
|
14875
|
+
route: params.route,
|
|
14876
|
+
thread: params.thread,
|
|
14877
|
+
streamState: params.streamState,
|
|
14878
|
+
payload
|
|
14879
|
+
});
|
|
14880
|
+
},
|
|
14881
|
+
onToolResult: (payload) => {
|
|
14882
|
+
deliverCowtailToolProgress({
|
|
14883
|
+
client: params.client,
|
|
14884
|
+
logger: params.logger,
|
|
14885
|
+
route: params.route,
|
|
14886
|
+
thread: params.thread,
|
|
14887
|
+
streamState: params.streamState,
|
|
14888
|
+
payload,
|
|
14889
|
+
status: "complete"
|
|
14890
|
+
});
|
|
14891
|
+
}
|
|
14758
14892
|
}
|
|
14759
14893
|
});
|
|
14760
14894
|
}
|
|
@@ -14774,10 +14908,20 @@ async function deliverCowtailReply(params) {
|
|
|
14774
14908
|
rawPayload: params.rawPayload,
|
|
14775
14909
|
streamState: params.streamState
|
|
14776
14910
|
});
|
|
14777
|
-
params.streamState
|
|
14911
|
+
upsertCowtailToolCall(params.streamState, toolCall);
|
|
14778
14912
|
const messageText = params.streamState.text;
|
|
14913
|
+
emitCowtailStreamSnapshot({
|
|
14914
|
+
client: params.client,
|
|
14915
|
+
logger: params.logger,
|
|
14916
|
+
route: params.route,
|
|
14917
|
+
thread: params.thread,
|
|
14918
|
+
streamState: params.streamState,
|
|
14919
|
+
text: currentLiveCowtailStreamText(params.streamState),
|
|
14920
|
+
isFinal: false,
|
|
14921
|
+
updatedAt: Date.now()
|
|
14922
|
+
});
|
|
14779
14923
|
if (!params.streamState.messageId) {
|
|
14780
|
-
const
|
|
14924
|
+
const result2 = await params.client.sendOpenClawMessage({
|
|
14781
14925
|
type: "openclaw_message",
|
|
14782
14926
|
idempotencyKey: params.streamState.idempotencyKey,
|
|
14783
14927
|
sessionKey: params.thread.sessionKey ?? params.route.sessionKey,
|
|
@@ -14789,11 +14933,11 @@ async function deliverCowtailReply(params) {
|
|
|
14789
14933
|
actions: [],
|
|
14790
14934
|
deliveryState: "pending"
|
|
14791
14935
|
});
|
|
14792
|
-
const
|
|
14793
|
-
if (!
|
|
14936
|
+
const messageId2 = recordCreatedOpenClawMessageId(params.streamState, result2);
|
|
14937
|
+
if (!messageId2) {
|
|
14794
14938
|
return;
|
|
14795
14939
|
}
|
|
14796
|
-
params.streamState.messageId =
|
|
14940
|
+
params.streamState.messageId = messageId2;
|
|
14797
14941
|
return;
|
|
14798
14942
|
}
|
|
14799
14943
|
await params.client.sendOpenClawMessageUpdate({
|
|
@@ -14812,8 +14956,18 @@ async function deliverCowtailReply(params) {
|
|
|
14812
14956
|
}
|
|
14813
14957
|
if (kind === "block") {
|
|
14814
14958
|
params.streamState.text = appendReplyBlock(params.streamState.text, text);
|
|
14959
|
+
emitCowtailStreamSnapshot({
|
|
14960
|
+
client: params.client,
|
|
14961
|
+
logger: params.logger,
|
|
14962
|
+
route: params.route,
|
|
14963
|
+
thread: params.thread,
|
|
14964
|
+
streamState: params.streamState,
|
|
14965
|
+
text: params.streamState.text,
|
|
14966
|
+
isFinal: false,
|
|
14967
|
+
updatedAt: Date.now()
|
|
14968
|
+
});
|
|
14815
14969
|
if (!params.streamState.messageId) {
|
|
14816
|
-
const
|
|
14970
|
+
const result2 = await params.client.sendOpenClawMessage({
|
|
14817
14971
|
type: "openclaw_message",
|
|
14818
14972
|
idempotencyKey: params.streamState.idempotencyKey,
|
|
14819
14973
|
sessionKey: params.thread.sessionKey ?? params.route.sessionKey,
|
|
@@ -14825,11 +14979,11 @@ async function deliverCowtailReply(params) {
|
|
|
14825
14979
|
actions: [],
|
|
14826
14980
|
deliveryState: "pending"
|
|
14827
14981
|
});
|
|
14828
|
-
const
|
|
14829
|
-
if (!
|
|
14982
|
+
const messageId2 = recordCreatedOpenClawMessageId(params.streamState, result2);
|
|
14983
|
+
if (!messageId2) {
|
|
14830
14984
|
return;
|
|
14831
14985
|
}
|
|
14832
|
-
params.streamState.messageId =
|
|
14986
|
+
params.streamState.messageId = messageId2;
|
|
14833
14987
|
return;
|
|
14834
14988
|
}
|
|
14835
14989
|
await params.client.sendOpenClawMessageUpdate({
|
|
@@ -14855,10 +15009,21 @@ async function deliverCowtailReply(params) {
|
|
|
14855
15009
|
actions: [],
|
|
14856
15010
|
deliveryState: "sent"
|
|
14857
15011
|
});
|
|
15012
|
+
emitCowtailStreamSnapshot({
|
|
15013
|
+
client: params.client,
|
|
15014
|
+
logger: params.logger,
|
|
15015
|
+
route: params.route,
|
|
15016
|
+
thread: params.thread,
|
|
15017
|
+
streamState: params.streamState,
|
|
15018
|
+
text,
|
|
15019
|
+
isFinal: true,
|
|
15020
|
+
updatedAt: Date.now()
|
|
15021
|
+
});
|
|
14858
15022
|
params.streamState.completed = true;
|
|
14859
15023
|
return;
|
|
14860
15024
|
}
|
|
14861
|
-
|
|
15025
|
+
params.streamState.text = text;
|
|
15026
|
+
const result = await params.client.sendOpenClawMessage({
|
|
14862
15027
|
type: "openclaw_message",
|
|
14863
15028
|
idempotencyKey: params.streamState.idempotencyKey,
|
|
14864
15029
|
sessionKey: params.thread.sessionKey ?? params.route.sessionKey,
|
|
@@ -14870,6 +15035,21 @@ async function deliverCowtailReply(params) {
|
|
|
14870
15035
|
actions: [],
|
|
14871
15036
|
deliveryState: "sent"
|
|
14872
15037
|
});
|
|
15038
|
+
const messageId = recordCreatedOpenClawMessageId(params.streamState, result);
|
|
15039
|
+
if (!messageId) {
|
|
15040
|
+
return;
|
|
15041
|
+
}
|
|
15042
|
+
params.streamState.messageId = messageId;
|
|
15043
|
+
emitCowtailStreamSnapshot({
|
|
15044
|
+
client: params.client,
|
|
15045
|
+
logger: params.logger,
|
|
15046
|
+
route: params.route,
|
|
15047
|
+
thread: params.thread,
|
|
15048
|
+
streamState: params.streamState,
|
|
15049
|
+
text,
|
|
15050
|
+
isFinal: true,
|
|
15051
|
+
updatedAt: Date.now()
|
|
15052
|
+
});
|
|
14873
15053
|
params.streamState.completed = true;
|
|
14874
15054
|
}
|
|
14875
15055
|
async function finalizeCowtailStreamedReply(params) {
|
|
@@ -14886,8 +15066,117 @@ async function finalizeCowtailStreamedReply(params) {
|
|
|
14886
15066
|
actions: [],
|
|
14887
15067
|
deliveryState: "sent"
|
|
14888
15068
|
});
|
|
15069
|
+
emitCowtailStreamSnapshot({
|
|
15070
|
+
client: params.client,
|
|
15071
|
+
logger: params.logger,
|
|
15072
|
+
route: params.route,
|
|
15073
|
+
thread: params.thread,
|
|
15074
|
+
streamState: params.streamState,
|
|
15075
|
+
text: params.streamState.text,
|
|
15076
|
+
isFinal: true,
|
|
15077
|
+
updatedAt: Date.now()
|
|
15078
|
+
});
|
|
14889
15079
|
params.streamState.completed = true;
|
|
14890
15080
|
}
|
|
15081
|
+
function deliverCowtailPartialReply(params) {
|
|
15082
|
+
const text = params.payload.text;
|
|
15083
|
+
if (params.streamState.failed || typeof text !== "string" || !text.trim()) {
|
|
15084
|
+
return;
|
|
15085
|
+
}
|
|
15086
|
+
const now = Date.now();
|
|
15087
|
+
const previous = params.streamState.lastSnapshotText ?? "";
|
|
15088
|
+
const changedChars = Math.abs(text.length - previous.length);
|
|
15089
|
+
const sentRecently = params.streamState.lastSnapshotSentAt !== undefined && now - params.streamState.lastSnapshotSentAt < STREAM_MIN_INTERVAL_MS;
|
|
15090
|
+
if (text === previous || sentRecently && changedChars < STREAM_MIN_CHARS_DELTA) {
|
|
15091
|
+
return;
|
|
15092
|
+
}
|
|
15093
|
+
emitCowtailStreamSnapshot({
|
|
15094
|
+
client: params.client,
|
|
15095
|
+
logger: params.logger,
|
|
15096
|
+
route: params.route,
|
|
15097
|
+
thread: params.thread,
|
|
15098
|
+
streamState: params.streamState,
|
|
15099
|
+
text,
|
|
15100
|
+
isFinal: false,
|
|
15101
|
+
updatedAt: now
|
|
15102
|
+
});
|
|
15103
|
+
}
|
|
15104
|
+
function deliverCowtailToolProgress(params) {
|
|
15105
|
+
if (params.streamState.failed) {
|
|
15106
|
+
return;
|
|
15107
|
+
}
|
|
15108
|
+
const payload = readRecord(params.payload) ?? {};
|
|
15109
|
+
const stableId = readString(payload.itemId) ?? readString(payload.toolCallId) ?? readString(payload.id);
|
|
15110
|
+
if (params.requireStableId && !stableId) {
|
|
15111
|
+
return;
|
|
15112
|
+
}
|
|
15113
|
+
const id = stableId ?? fallbackCowtailToolCallId(params.streamState, payload);
|
|
15114
|
+
const name = readString(payload.name) ?? readString(payload.title) ?? readString(payload.command) ?? "tool_result";
|
|
15115
|
+
const status = params.status ?? normalizeCowtailToolCallStatus(payload.status) ?? normalizeCowtailToolCallStatus(payload.phase) ?? "running";
|
|
15116
|
+
const result = readCowtailToolProgressResult(payload);
|
|
15117
|
+
const args = readRecord(payload.args) ?? readRecord(payload.input);
|
|
15118
|
+
const now = Date.now();
|
|
15119
|
+
const currentText = currentLiveCowtailStreamText(params.streamState);
|
|
15120
|
+
const nextToolCall = {
|
|
15121
|
+
id,
|
|
15122
|
+
name,
|
|
15123
|
+
...args ? { args } : {},
|
|
15124
|
+
...result !== undefined ? { result } : {},
|
|
15125
|
+
status,
|
|
15126
|
+
...status === "complete" || status === "error" ? { completedAt: now } : {},
|
|
15127
|
+
insertedAtContentLength: currentText.length,
|
|
15128
|
+
contentSnapshotAtStart: currentText
|
|
15129
|
+
};
|
|
15130
|
+
upsertCowtailToolCall(params.streamState, nextToolCall);
|
|
15131
|
+
emitCowtailStreamSnapshot({
|
|
15132
|
+
client: params.client,
|
|
15133
|
+
logger: params.logger,
|
|
15134
|
+
route: params.route,
|
|
15135
|
+
thread: params.thread,
|
|
15136
|
+
streamState: params.streamState,
|
|
15137
|
+
text: currentText,
|
|
15138
|
+
isFinal: false,
|
|
15139
|
+
updatedAt: now
|
|
15140
|
+
});
|
|
15141
|
+
}
|
|
15142
|
+
function emitCowtailStreamSnapshot(params) {
|
|
15143
|
+
if (params.streamState.failed || !params.text.trim() && params.streamState.links.length === 0 && params.streamState.toolCalls.length === 0) {
|
|
15144
|
+
return;
|
|
15145
|
+
}
|
|
15146
|
+
params.streamState.liveText = params.text;
|
|
15147
|
+
params.streamState.lastSnapshotText = params.text;
|
|
15148
|
+
params.streamState.lastSnapshotSentAt = params.updatedAt;
|
|
15149
|
+
params.client.sendOpenClawStreamSnapshot({
|
|
15150
|
+
type: "openclaw_message_stream_snapshot",
|
|
15151
|
+
streamId: params.streamState.streamId,
|
|
15152
|
+
sessionKey: params.thread.sessionKey ?? params.route.sessionKey,
|
|
15153
|
+
threadId: params.thread.id,
|
|
15154
|
+
text: params.text,
|
|
15155
|
+
links: params.streamState.links.map((link) => ({ ...link })),
|
|
15156
|
+
toolCalls: structuredClone(params.streamState.toolCalls),
|
|
15157
|
+
isFinal: params.isFinal,
|
|
15158
|
+
updatedAt: params.updatedAt
|
|
15159
|
+
}).catch((error48) => {
|
|
15160
|
+
params.logger?.warn?.(`Cowtail stream snapshot send failed: ${errorMessage(error48)}`);
|
|
15161
|
+
});
|
|
15162
|
+
}
|
|
15163
|
+
function currentLiveCowtailStreamText(streamState) {
|
|
15164
|
+
return streamState.liveText ?? streamState.text;
|
|
15165
|
+
}
|
|
15166
|
+
function upsertCowtailToolCall(streamState, toolCall) {
|
|
15167
|
+
const index = streamState.toolCalls.findIndex((candidate) => candidate.id === toolCall.id);
|
|
15168
|
+
if (index < 0) {
|
|
15169
|
+
streamState.toolCalls = [...streamState.toolCalls, toolCall];
|
|
15170
|
+
return;
|
|
15171
|
+
}
|
|
15172
|
+
const existing = streamState.toolCalls[index];
|
|
15173
|
+
streamState.toolCalls[index] = {
|
|
15174
|
+
...existing,
|
|
15175
|
+
...toolCall,
|
|
15176
|
+
insertedAtContentLength: existing.insertedAtContentLength ?? toolCall.insertedAtContentLength,
|
|
15177
|
+
contentSnapshotAtStart: existing.contentSnapshotAtStart ?? toolCall.contentSnapshotAtStart
|
|
15178
|
+
};
|
|
15179
|
+
}
|
|
14891
15180
|
function recordCreatedOpenClawMessageId(streamState, result) {
|
|
14892
15181
|
if (result.payload?.dropped === true) {
|
|
14893
15182
|
if (result.payload.duplicate === true) {
|
|
@@ -14983,6 +15272,48 @@ function readRecord(value) {
|
|
|
14983
15272
|
function readToolCallStatus(value) {
|
|
14984
15273
|
return value === "pending" || value === "running" || value === "complete" || value === "error" ? value : undefined;
|
|
14985
15274
|
}
|
|
15275
|
+
function normalizeCowtailToolCallStatus(value) {
|
|
15276
|
+
const status = readToolCallStatus(value);
|
|
15277
|
+
if (status) {
|
|
15278
|
+
return status;
|
|
15279
|
+
}
|
|
15280
|
+
if (value === "start" || value === "started" || value === "working") {
|
|
15281
|
+
return "running";
|
|
15282
|
+
}
|
|
15283
|
+
if (value === "end" || value === "done" || value === "completed" || value === "success") {
|
|
15284
|
+
return "complete";
|
|
15285
|
+
}
|
|
15286
|
+
if (value === "failed" || value === "failure") {
|
|
15287
|
+
return "error";
|
|
15288
|
+
}
|
|
15289
|
+
return;
|
|
15290
|
+
}
|
|
15291
|
+
function readCowtailToolProgressResult(payload) {
|
|
15292
|
+
const stringResult = readString(payload.output) ?? readString(payload.summary) ?? readString(payload.message) ?? readString(payload.progressText) ?? readString(payload.text);
|
|
15293
|
+
if (stringResult !== undefined) {
|
|
15294
|
+
return stringResult;
|
|
15295
|
+
}
|
|
15296
|
+
if (payload.result !== undefined) {
|
|
15297
|
+
return payload.result;
|
|
15298
|
+
}
|
|
15299
|
+
const structuredPatch = {
|
|
15300
|
+
...Array.isArray(payload.added) ? { added: payload.added } : {},
|
|
15301
|
+
...Array.isArray(payload.modified) ? { modified: payload.modified } : {},
|
|
15302
|
+
...Array.isArray(payload.deleted) ? { deleted: payload.deleted } : {}
|
|
15303
|
+
};
|
|
15304
|
+
return Object.keys(structuredPatch).length > 0 ? structuredPatch : undefined;
|
|
15305
|
+
}
|
|
15306
|
+
function fallbackCowtailToolCallId(streamState, payload) {
|
|
15307
|
+
const key = readString(payload.name) ?? readString(payload.title) ?? readString(payload.command) ?? JSON.stringify(Object.keys(payload).sort());
|
|
15308
|
+
const existing = streamState.toolFallbackIds[key];
|
|
15309
|
+
if (existing) {
|
|
15310
|
+
return existing;
|
|
15311
|
+
}
|
|
15312
|
+
streamState.toolFallbackIndex += 1;
|
|
15313
|
+
const next = `tool-${streamState.toolFallbackIndex}`;
|
|
15314
|
+
streamState.toolFallbackIds[key] = next;
|
|
15315
|
+
return next;
|
|
15316
|
+
}
|
|
14986
15317
|
function readTimestamp(value) {
|
|
14987
15318
|
return typeof value === "number" && Number.isInteger(value) && value >= 0 ? value : undefined;
|
|
14988
15319
|
}
|