@maudecode/openclaw-cowtail 0.13.0 → 0.14.1
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 +4 -0
- package/dist/index.js +180 -59
- package/dist/setup-entry.js +180 -59
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -7,3 +7,7 @@ Load it from an OpenClaw config with `plugins.entries.cowtail.path` pointing at
|
|
|
7
7
|
Configure `channels.cowtail.url` with the Cowtail realtime WebSocket endpoint and `channels.cowtail.bridgeToken` with the same bridge token configured on the Cowtail realtime service.
|
|
8
8
|
|
|
9
9
|
V1 supports a single owner and OpenClaw's default `main` agent only.
|
|
10
|
+
|
|
11
|
+
## Realtime event scope
|
|
12
|
+
|
|
13
|
+
The plugin receives Cowtail events that require OpenClaw work: iOS-created threads, iOS replies, and submitted actions. Cowtail thread lifecycle updates such as read state, rename, and delete stay local to Cowtail/iOS because OpenClaw v1 has no channel-level state mutation contract for those operations.
|
package/dist/index.js
CHANGED
|
@@ -13801,6 +13801,7 @@ var openclawThreadRecordSchema = exports_external.object({
|
|
|
13801
13801
|
var openclawMessageRecordBaseSchema = exports_external.object({
|
|
13802
13802
|
id: nonEmptyStringSchema,
|
|
13803
13803
|
threadId: nonEmptyStringSchema,
|
|
13804
|
+
streamId: nonEmptyStringSchema.optional(),
|
|
13804
13805
|
direction: openclawMessageDirectionSchema,
|
|
13805
13806
|
authorLabel: nonEmptyStringSchema.optional(),
|
|
13806
13807
|
text: openclawMessageTextSchema,
|
|
@@ -13855,13 +13856,16 @@ var openclawPluginMessageCommandSchema = requireOpenClawRenderableContent(export
|
|
|
13855
13856
|
requestId: openclawRequestIdSchema,
|
|
13856
13857
|
idempotencyKey: openclawIdempotencyKeySchema,
|
|
13857
13858
|
sessionKey: nonEmptyStringSchema,
|
|
13859
|
+
threadId: nonEmptyStringSchema.optional(),
|
|
13860
|
+
threadHint: nonEmptyStringSchema.optional(),
|
|
13858
13861
|
title: nonEmptyStringSchema.optional(),
|
|
13859
13862
|
text: openclawMessageTextSchema,
|
|
13860
13863
|
authorLabel: nonEmptyStringSchema.optional(),
|
|
13861
13864
|
links: exports_external.array(openclawLinkSchema).default([]),
|
|
13862
13865
|
toolCalls: exports_external.array(openclawToolCallRecordSchema).default([]),
|
|
13863
13866
|
actions: exports_external.array(openclawActionDraftSchema).default([]),
|
|
13864
|
-
deliveryState: openclawDeliveryStateSchema.optional()
|
|
13867
|
+
deliveryState: openclawDeliveryStateSchema.optional(),
|
|
13868
|
+
streamId: nonEmptyStringSchema
|
|
13865
13869
|
}));
|
|
13866
13870
|
var openclawPluginMessageUpdateCommandSchema = requireOpenClawRenderableContent(exports_external.object({
|
|
13867
13871
|
type: exports_external.literal("openclaw_message_update"),
|
|
@@ -13872,7 +13876,8 @@ var openclawPluginMessageUpdateCommandSchema = requireOpenClawRenderableContent(
|
|
|
13872
13876
|
links: exports_external.array(openclawLinkSchema).optional(),
|
|
13873
13877
|
toolCalls: exports_external.array(openclawToolCallRecordSchema).optional(),
|
|
13874
13878
|
actions: exports_external.array(openclawActionDraftSchema).optional(),
|
|
13875
|
-
deliveryState: openclawDeliveryStateSchema.optional()
|
|
13879
|
+
deliveryState: openclawDeliveryStateSchema.optional(),
|
|
13880
|
+
streamId: nonEmptyStringSchema
|
|
13876
13881
|
}));
|
|
13877
13882
|
var openclawMessageStreamSnapshotCommandBaseSchema = exports_external.object({
|
|
13878
13883
|
type: exports_external.literal("openclaw_message_stream_snapshot"),
|
|
@@ -13884,6 +13889,7 @@ var openclawMessageStreamSnapshotCommandBaseSchema = exports_external.object({
|
|
|
13884
13889
|
links: exports_external.array(openclawLinkSchema).default([]),
|
|
13885
13890
|
toolCalls: exports_external.array(openclawToolCallRecordSchema).default([]),
|
|
13886
13891
|
isFinal: exports_external.boolean(),
|
|
13892
|
+
snapshotSequence: exports_external.number().int().nonnegative(),
|
|
13887
13893
|
updatedAt: timestampSchema
|
|
13888
13894
|
});
|
|
13889
13895
|
var openclawMessageStreamSnapshotCommandSchema = requireOpenClawRenderableContent(openclawMessageStreamSnapshotCommandBaseSchema);
|
|
@@ -13956,11 +13962,18 @@ var openclawRealtimeClientMessageSchema = exports_external.union([
|
|
|
13956
13962
|
openclawSessionBoundCommandSchema,
|
|
13957
13963
|
openclawActionResultCommandSchema
|
|
13958
13964
|
]);
|
|
13965
|
+
var openclawRealtimeAckPayloadSchema = exports_external.object({
|
|
13966
|
+
threadId: nonEmptyStringSchema.optional(),
|
|
13967
|
+
messageId: nonEmptyStringSchema.optional(),
|
|
13968
|
+
dropped: exports_external.literal(true).optional(),
|
|
13969
|
+
duplicate: exports_external.literal(true).optional(),
|
|
13970
|
+
reason: nonEmptyStringSchema.optional()
|
|
13971
|
+
});
|
|
13959
13972
|
var openclawRealtimeAckSchema = exports_external.object({
|
|
13960
13973
|
type: exports_external.literal("ack"),
|
|
13961
13974
|
requestId: openclawRequestIdSchema,
|
|
13962
13975
|
sequence: openclawSequenceSchema.optional(),
|
|
13963
|
-
payload:
|
|
13976
|
+
payload: openclawRealtimeAckPayloadSchema.optional()
|
|
13964
13977
|
});
|
|
13965
13978
|
var openclawRealtimeErrorSchema = exports_external.object({
|
|
13966
13979
|
type: exports_external.literal("realtime_error"),
|
|
@@ -14009,6 +14022,13 @@ var openclawEventReplayResponseSchema = exports_external.object({
|
|
|
14009
14022
|
ok: exports_external.literal(true),
|
|
14010
14023
|
events: exports_external.array(openclawEventEnvelopeSchema)
|
|
14011
14024
|
});
|
|
14025
|
+
var openclawPushNotificationPayloadSchema = exports_external.object({
|
|
14026
|
+
kind: exports_external.literal("openclaw"),
|
|
14027
|
+
version: exports_external.literal(1),
|
|
14028
|
+
threadId: nonEmptyStringSchema,
|
|
14029
|
+
messageId: nonEmptyStringSchema,
|
|
14030
|
+
url: nonEmptyStringSchema.optional()
|
|
14031
|
+
});
|
|
14012
14032
|
// ../protocol/src/fixes.ts
|
|
14013
14033
|
var fixScopeSchema = exports_external.enum(fixScopes);
|
|
14014
14034
|
var fixCreateRequestSchema = exports_external.object({
|
|
@@ -14064,11 +14084,12 @@ var healthResponseSchema = exports_external.object({
|
|
|
14064
14084
|
storageUnit: nonEmptyStringSchema
|
|
14065
14085
|
});
|
|
14066
14086
|
// ../protocol/src/push.ts
|
|
14087
|
+
var pushEnvironmentSchema = exports_external.enum(["development", "production"]);
|
|
14067
14088
|
var pushRegisterRequestSchema = exports_external.object({
|
|
14068
14089
|
identityToken: nonEmptyStringSchema,
|
|
14069
14090
|
deviceToken: nonEmptyStringSchema,
|
|
14070
14091
|
platform: nonEmptyStringSchema.optional(),
|
|
14071
|
-
environment:
|
|
14092
|
+
environment: pushEnvironmentSchema.optional(),
|
|
14072
14093
|
deviceName: nonEmptyStringSchema.optional()
|
|
14073
14094
|
});
|
|
14074
14095
|
var pushRegisterResponseSchema = exports_external.object({
|
|
@@ -14077,6 +14098,7 @@ var pushRegisterResponseSchema = exports_external.object({
|
|
|
14077
14098
|
id: nonEmptyStringSchema
|
|
14078
14099
|
});
|
|
14079
14100
|
var pushUnregisterRequestSchema = exports_external.object({
|
|
14101
|
+
identityToken: nonEmptyStringSchema,
|
|
14080
14102
|
deviceToken: nonEmptyStringSchema
|
|
14081
14103
|
});
|
|
14082
14104
|
var pushUnregisterResponseSchema = exports_external.object({
|
|
@@ -14100,6 +14122,7 @@ var pushResultSchema = exports_external.object({
|
|
|
14100
14122
|
userId: nonEmptyStringSchema,
|
|
14101
14123
|
sent: exports_external.number().int().nonnegative(),
|
|
14102
14124
|
failed: exports_external.number().int().nonnegative(),
|
|
14125
|
+
skipped: exports_external.number().int().nonnegative().default(0),
|
|
14103
14126
|
results: exports_external.array(jsonObjectSchema)
|
|
14104
14127
|
});
|
|
14105
14128
|
// ../protocol/src/responses.ts
|
|
@@ -14120,7 +14143,8 @@ var usersListEntrySchema = exports_external.object({
|
|
|
14120
14143
|
enabledDeviceCount: exports_external.number().int().nonnegative()
|
|
14121
14144
|
});
|
|
14122
14145
|
var userDeviceSchema = exports_external.object({
|
|
14123
|
-
|
|
14146
|
+
id: nonEmptyStringSchema,
|
|
14147
|
+
deviceTokenPreview: nonEmptyStringSchema,
|
|
14124
14148
|
platform: nonEmptyStringSchema,
|
|
14125
14149
|
environment: nonEmptyStringSchema,
|
|
14126
14150
|
enabled: exports_external.boolean(),
|
|
@@ -14192,10 +14216,12 @@ class CowtailRealtimeClient {
|
|
|
14192
14216
|
}
|
|
14193
14217
|
sendOpenClawMessage(command) {
|
|
14194
14218
|
const requestId = this.#requestIdFactory();
|
|
14219
|
+
const idempotencyKey = command.idempotencyKey ?? `cowtail:request:${requestId}`;
|
|
14195
14220
|
return this.#sendCommand({
|
|
14196
14221
|
...command,
|
|
14197
14222
|
requestId,
|
|
14198
|
-
idempotencyKey
|
|
14223
|
+
idempotencyKey,
|
|
14224
|
+
streamId: command.streamId ?? idempotencyKey,
|
|
14199
14225
|
links: command.links ?? [],
|
|
14200
14226
|
toolCalls: command.toolCalls ?? [],
|
|
14201
14227
|
actions: command.actions ?? []
|
|
@@ -14519,6 +14545,30 @@ function normalizeCowtailTarget(raw) {
|
|
|
14519
14545
|
function buildCowtailTarget(threadId) {
|
|
14520
14546
|
return `cowtail:${normalizeCowtailTarget(threadId)}`;
|
|
14521
14547
|
}
|
|
14548
|
+
function resolveCowtailThreadIdFromSessionKey(sessionKey) {
|
|
14549
|
+
const raw = (sessionKey ?? "").trim();
|
|
14550
|
+
if (!raw) {
|
|
14551
|
+
return;
|
|
14552
|
+
}
|
|
14553
|
+
const parts = raw.split(":").filter(Boolean);
|
|
14554
|
+
if (parts.length < 4 || parts[0]?.toLowerCase() !== "agent") {
|
|
14555
|
+
return;
|
|
14556
|
+
}
|
|
14557
|
+
const cowtailIndex = parts.findIndex((part, index) => index >= 2 && part.toLowerCase() === "cowtail");
|
|
14558
|
+
if (cowtailIndex < 0) {
|
|
14559
|
+
if (parts[2]?.toLowerCase() !== "direct") {
|
|
14560
|
+
return;
|
|
14561
|
+
}
|
|
14562
|
+
const directTarget = normalizeCowtailTarget(parts.slice(3).join(":"));
|
|
14563
|
+
return directTarget || undefined;
|
|
14564
|
+
}
|
|
14565
|
+
const rest = parts.slice(cowtailIndex + 1);
|
|
14566
|
+
const first = rest[0]?.toLowerCase();
|
|
14567
|
+
const second = rest[1]?.toLowerCase();
|
|
14568
|
+
const target = first === "direct" || first === "group" || first === "channel" ? rest.slice(1).join(":") : second === "direct" || second === "group" || second === "channel" ? rest.slice(2).join(":") : rest.join(":");
|
|
14569
|
+
const normalized = normalizeCowtailTarget(target);
|
|
14570
|
+
return normalized || undefined;
|
|
14571
|
+
}
|
|
14522
14572
|
function isSupportedCowtailAgent(agentId) {
|
|
14523
14573
|
return (agentId ?? "main").trim() === "main";
|
|
14524
14574
|
}
|
|
@@ -14555,10 +14605,11 @@ async function handleCowtailEvent(params) {
|
|
|
14555
14605
|
return;
|
|
14556
14606
|
}
|
|
14557
14607
|
if (!thread.sessionKey) {
|
|
14608
|
+
const threadId = resolveCowtailThreadId({ route, thread });
|
|
14558
14609
|
await client.sendSessionBound({
|
|
14559
14610
|
type: "openclaw_session_bound",
|
|
14560
|
-
idempotencyKey: `cowtail:session-bound:${
|
|
14561
|
-
threadId
|
|
14611
|
+
idempotencyKey: `cowtail:session-bound:${threadId}`,
|
|
14612
|
+
threadId,
|
|
14562
14613
|
sessionKey: route.sessionKey
|
|
14563
14614
|
});
|
|
14564
14615
|
}
|
|
@@ -14668,6 +14719,7 @@ async function dispatchCowtailTextTurn(params) {
|
|
|
14668
14719
|
idempotencyKey: `cowtail:reply:${message.id}`,
|
|
14669
14720
|
streamId: `cowtail:stream:${message.id}`,
|
|
14670
14721
|
updateIndex: 0,
|
|
14722
|
+
snapshotSequence: 0,
|
|
14671
14723
|
toolFallbackIndex: 0,
|
|
14672
14724
|
toolFallbackIds: {},
|
|
14673
14725
|
text: "",
|
|
@@ -14741,6 +14793,7 @@ async function dispatchCowtailActionTurn(params) {
|
|
|
14741
14793
|
idempotencyKey: `cowtail:action:${action.id}`,
|
|
14742
14794
|
streamId: `cowtail:action-stream:${action.id}`,
|
|
14743
14795
|
updateIndex: 0,
|
|
14796
|
+
snapshotSequence: 0,
|
|
14744
14797
|
toolFallbackIndex: 0,
|
|
14745
14798
|
toolFallbackIds: {},
|
|
14746
14799
|
text: "",
|
|
@@ -14921,28 +14974,17 @@ async function deliverCowtailReply(params) {
|
|
|
14921
14974
|
updatedAt: Date.now()
|
|
14922
14975
|
});
|
|
14923
14976
|
if (!params.streamState.messageId) {
|
|
14924
|
-
|
|
14925
|
-
type: "openclaw_message",
|
|
14926
|
-
idempotencyKey: params.streamState.idempotencyKey,
|
|
14927
|
-
sessionKey: params.thread.sessionKey ?? params.route.sessionKey,
|
|
14928
|
-
title: params.thread.title,
|
|
14977
|
+
await createDurableCowtailReply(params, {
|
|
14929
14978
|
text: messageText,
|
|
14930
|
-
authorLabel: "OpenClaw",
|
|
14931
14979
|
links,
|
|
14932
|
-
toolCalls: params.streamState.toolCalls,
|
|
14933
|
-
actions: [],
|
|
14934
14980
|
deliveryState: "pending"
|
|
14935
14981
|
});
|
|
14936
|
-
const messageId2 = recordCreatedOpenClawMessageId(params.streamState, result2);
|
|
14937
|
-
if (!messageId2) {
|
|
14938
|
-
return;
|
|
14939
|
-
}
|
|
14940
|
-
params.streamState.messageId = messageId2;
|
|
14941
14982
|
return;
|
|
14942
14983
|
}
|
|
14943
14984
|
await params.client.sendOpenClawMessageUpdate({
|
|
14944
14985
|
type: "openclaw_message_update",
|
|
14945
14986
|
idempotencyKey: nextStreamUpdateIdempotencyKey(params.streamState),
|
|
14987
|
+
streamId: params.streamState.streamId,
|
|
14946
14988
|
messageId: params.streamState.messageId,
|
|
14947
14989
|
text: messageText,
|
|
14948
14990
|
links,
|
|
@@ -14967,28 +15009,17 @@ async function deliverCowtailReply(params) {
|
|
|
14967
15009
|
updatedAt: Date.now()
|
|
14968
15010
|
});
|
|
14969
15011
|
if (!params.streamState.messageId) {
|
|
14970
|
-
|
|
14971
|
-
type: "openclaw_message",
|
|
14972
|
-
idempotencyKey: params.streamState.idempotencyKey,
|
|
14973
|
-
sessionKey: params.thread.sessionKey ?? params.route.sessionKey,
|
|
14974
|
-
title: params.thread.title,
|
|
15012
|
+
await createDurableCowtailReply(params, {
|
|
14975
15013
|
text: params.streamState.text,
|
|
14976
|
-
authorLabel: "OpenClaw",
|
|
14977
15014
|
links,
|
|
14978
|
-
toolCalls: params.streamState.toolCalls,
|
|
14979
|
-
actions: [],
|
|
14980
15015
|
deliveryState: "pending"
|
|
14981
15016
|
});
|
|
14982
|
-
const messageId2 = recordCreatedOpenClawMessageId(params.streamState, result2);
|
|
14983
|
-
if (!messageId2) {
|
|
14984
|
-
return;
|
|
14985
|
-
}
|
|
14986
|
-
params.streamState.messageId = messageId2;
|
|
14987
15017
|
return;
|
|
14988
15018
|
}
|
|
14989
15019
|
await params.client.sendOpenClawMessageUpdate({
|
|
14990
15020
|
type: "openclaw_message_update",
|
|
14991
15021
|
idempotencyKey: nextStreamUpdateIdempotencyKey(params.streamState),
|
|
15022
|
+
streamId: params.streamState.streamId,
|
|
14992
15023
|
messageId: params.streamState.messageId,
|
|
14993
15024
|
text: params.streamState.text,
|
|
14994
15025
|
links,
|
|
@@ -15002,6 +15033,7 @@ async function deliverCowtailReply(params) {
|
|
|
15002
15033
|
await params.client.sendOpenClawMessageUpdate({
|
|
15003
15034
|
type: "openclaw_message_update",
|
|
15004
15035
|
idempotencyKey: nextStreamUpdateIdempotencyKey(params.streamState),
|
|
15036
|
+
streamId: params.streamState.streamId,
|
|
15005
15037
|
messageId: params.streamState.messageId,
|
|
15006
15038
|
text,
|
|
15007
15039
|
links,
|
|
@@ -15023,23 +15055,14 @@ async function deliverCowtailReply(params) {
|
|
|
15023
15055
|
return;
|
|
15024
15056
|
}
|
|
15025
15057
|
params.streamState.text = text;
|
|
15026
|
-
const
|
|
15027
|
-
type: "openclaw_message",
|
|
15028
|
-
idempotencyKey: params.streamState.idempotencyKey,
|
|
15029
|
-
sessionKey: params.thread.sessionKey ?? params.route.sessionKey,
|
|
15030
|
-
title: params.thread.title,
|
|
15058
|
+
const messageId = await createDurableCowtailReply(params, {
|
|
15031
15059
|
text,
|
|
15032
|
-
authorLabel: "OpenClaw",
|
|
15033
15060
|
links,
|
|
15034
|
-
toolCalls: params.streamState.toolCalls,
|
|
15035
|
-
actions: [],
|
|
15036
15061
|
deliveryState: "sent"
|
|
15037
15062
|
});
|
|
15038
|
-
const messageId = recordCreatedOpenClawMessageId(params.streamState, result);
|
|
15039
15063
|
if (!messageId) {
|
|
15040
15064
|
return;
|
|
15041
15065
|
}
|
|
15042
|
-
params.streamState.messageId = messageId;
|
|
15043
15066
|
emitCowtailStreamSnapshot({
|
|
15044
15067
|
client: params.client,
|
|
15045
15068
|
logger: params.logger,
|
|
@@ -15052,6 +15075,35 @@ async function deliverCowtailReply(params) {
|
|
|
15052
15075
|
});
|
|
15053
15076
|
params.streamState.completed = true;
|
|
15054
15077
|
}
|
|
15078
|
+
async function createDurableCowtailReply(params, message) {
|
|
15079
|
+
try {
|
|
15080
|
+
const routedThreadId = resolveCowtailThreadIdFromSessionKey(params.route.sessionKey);
|
|
15081
|
+
const result = await params.client.sendOpenClawMessage({
|
|
15082
|
+
type: "openclaw_message",
|
|
15083
|
+
idempotencyKey: params.streamState.idempotencyKey,
|
|
15084
|
+
streamId: params.streamState.streamId,
|
|
15085
|
+
sessionKey: params.route.sessionKey,
|
|
15086
|
+
...routedThreadId ? { threadId: routedThreadId } : {},
|
|
15087
|
+
title: params.thread.title,
|
|
15088
|
+
text: message.text,
|
|
15089
|
+
authorLabel: "OpenClaw",
|
|
15090
|
+
links: message.links,
|
|
15091
|
+
toolCalls: params.streamState.toolCalls,
|
|
15092
|
+
actions: [],
|
|
15093
|
+
deliveryState: message.deliveryState
|
|
15094
|
+
});
|
|
15095
|
+
const messageId = recordCreatedOpenClawMessageId(params.streamState, result);
|
|
15096
|
+
if (!messageId) {
|
|
15097
|
+
failCowtailStreamBeforeDurableCreate(params);
|
|
15098
|
+
return;
|
|
15099
|
+
}
|
|
15100
|
+
params.streamState.messageId = messageId;
|
|
15101
|
+
return messageId;
|
|
15102
|
+
} catch (error48) {
|
|
15103
|
+
failCowtailStreamBeforeDurableCreate(params);
|
|
15104
|
+
throw error48;
|
|
15105
|
+
}
|
|
15106
|
+
}
|
|
15055
15107
|
async function finalizeCowtailStreamedReply(params) {
|
|
15056
15108
|
if (params.streamState.failed || params.streamState.completed || !params.streamState.messageId || !params.streamState.text.trim() && params.streamState.toolCalls.length === 0) {
|
|
15057
15109
|
return;
|
|
@@ -15059,6 +15111,7 @@ async function finalizeCowtailStreamedReply(params) {
|
|
|
15059
15111
|
await params.client.sendOpenClawMessageUpdate({
|
|
15060
15112
|
type: "openclaw_message_update",
|
|
15061
15113
|
idempotencyKey: nextStreamUpdateIdempotencyKey(params.streamState),
|
|
15114
|
+
streamId: params.streamState.streamId,
|
|
15062
15115
|
messageId: params.streamState.messageId,
|
|
15063
15116
|
text: params.streamState.text,
|
|
15064
15117
|
links: params.streamState.links,
|
|
@@ -15140,26 +15193,46 @@ function deliverCowtailToolProgress(params) {
|
|
|
15140
15193
|
});
|
|
15141
15194
|
}
|
|
15142
15195
|
function emitCowtailStreamSnapshot(params) {
|
|
15143
|
-
if (params.streamState.failed || !params.text.trim() && params.streamState.links.length === 0 && params.streamState.toolCalls.length === 0) {
|
|
15196
|
+
if (params.streamState.failed && params.allowFailed !== true || !params.text.trim() && params.streamState.links.length === 0 && params.streamState.toolCalls.length === 0) {
|
|
15144
15197
|
return;
|
|
15145
15198
|
}
|
|
15146
15199
|
params.streamState.liveText = params.text;
|
|
15147
15200
|
params.streamState.lastSnapshotText = params.text;
|
|
15148
15201
|
params.streamState.lastSnapshotSentAt = params.updatedAt;
|
|
15202
|
+
params.streamState.snapshotSequence += 1;
|
|
15149
15203
|
params.client.sendOpenClawStreamSnapshot({
|
|
15150
15204
|
type: "openclaw_message_stream_snapshot",
|
|
15151
15205
|
streamId: params.streamState.streamId,
|
|
15152
|
-
sessionKey: params.
|
|
15153
|
-
threadId: params
|
|
15206
|
+
sessionKey: params.route.sessionKey,
|
|
15207
|
+
threadId: resolveCowtailThreadId(params),
|
|
15154
15208
|
text: params.text,
|
|
15155
15209
|
links: params.streamState.links.map((link) => ({ ...link })),
|
|
15156
15210
|
toolCalls: structuredClone(params.streamState.toolCalls),
|
|
15157
15211
|
isFinal: params.isFinal,
|
|
15212
|
+
snapshotSequence: params.streamState.snapshotSequence,
|
|
15158
15213
|
updatedAt: params.updatedAt
|
|
15159
15214
|
}).catch((error48) => {
|
|
15160
15215
|
params.logger?.warn?.(`Cowtail stream snapshot send failed: ${errorMessage(error48)}`);
|
|
15161
15216
|
});
|
|
15162
15217
|
}
|
|
15218
|
+
function failCowtailStreamBeforeDurableCreate(params) {
|
|
15219
|
+
if (!params.streamState.messageId && params.streamState.snapshotSequence > 0) {
|
|
15220
|
+
const text = params.streamState.text.trim() ? params.streamState.text : currentLiveCowtailStreamText(params.streamState);
|
|
15221
|
+
emitCowtailStreamSnapshot({
|
|
15222
|
+
client: params.client,
|
|
15223
|
+
logger: params.logger,
|
|
15224
|
+
route: params.route,
|
|
15225
|
+
thread: params.thread,
|
|
15226
|
+
streamState: params.streamState,
|
|
15227
|
+
text,
|
|
15228
|
+
isFinal: true,
|
|
15229
|
+
updatedAt: Date.now(),
|
|
15230
|
+
allowFailed: true
|
|
15231
|
+
});
|
|
15232
|
+
}
|
|
15233
|
+
params.streamState.failed = true;
|
|
15234
|
+
params.streamState.completed = true;
|
|
15235
|
+
}
|
|
15163
15236
|
function currentLiveCowtailStreamText(streamState) {
|
|
15164
15237
|
return streamState.liveText ?? streamState.text;
|
|
15165
15238
|
}
|
|
@@ -15357,7 +15430,7 @@ function buildCowtailInboundBody(params) {
|
|
|
15357
15430
|
};
|
|
15358
15431
|
}
|
|
15359
15432
|
function finalizeInboundContext(params) {
|
|
15360
|
-
const target = buildCowtailTarget(params
|
|
15433
|
+
const target = buildCowtailTarget(resolveCowtailThreadId(params));
|
|
15361
15434
|
return params.runtime.channel.reply.finalizeInboundContext({
|
|
15362
15435
|
Body: params.body,
|
|
15363
15436
|
BodyForAgent: params.text,
|
|
@@ -15401,14 +15474,17 @@ function resolveThreadRoute(params) {
|
|
|
15401
15474
|
sessionKey: params.thread.sessionKey
|
|
15402
15475
|
};
|
|
15403
15476
|
}
|
|
15404
|
-
const
|
|
15405
|
-
|
|
15406
|
-
|
|
15477
|
+
const threadId = normalizeCowtailTarget(params.thread.id);
|
|
15478
|
+
if (!threadId) {
|
|
15479
|
+
params.logger?.warn?.("Cowtail thread_created event has a blank thread id");
|
|
15480
|
+
return null;
|
|
15481
|
+
}
|
|
15482
|
+
const cfg = params.runtime.config.loadConfig();
|
|
15483
|
+
const route = resolveCowtailAgentRoute({
|
|
15484
|
+
cfg,
|
|
15485
|
+
threadId,
|
|
15407
15486
|
accountId: params.account.accountId,
|
|
15408
|
-
|
|
15409
|
-
kind: "direct",
|
|
15410
|
-
id: buildCowtailTarget(params.thread.id)
|
|
15411
|
-
}
|
|
15487
|
+
runtime: params.runtime
|
|
15412
15488
|
});
|
|
15413
15489
|
if (!isSupportedCowtailAgent(route.agentId)) {
|
|
15414
15490
|
params.logger?.warn?.(`Cowtail thread ${params.thread.id} resolved unsupported agent ${route.agentId}`);
|
|
@@ -15419,6 +15495,45 @@ function resolveThreadRoute(params) {
|
|
|
15419
15495
|
sessionKey: route.sessionKey
|
|
15420
15496
|
};
|
|
15421
15497
|
}
|
|
15498
|
+
function resolveCowtailAgentRoute(params) {
|
|
15499
|
+
const rawRoute = resolveAgentRouteForPeer({
|
|
15500
|
+
cfg: params.cfg,
|
|
15501
|
+
accountId: params.accountId,
|
|
15502
|
+
runtime: params.runtime,
|
|
15503
|
+
peerId: params.threadId
|
|
15504
|
+
});
|
|
15505
|
+
if (isPeerBindingRoute(rawRoute)) {
|
|
15506
|
+
return rawRoute;
|
|
15507
|
+
}
|
|
15508
|
+
const prefixedThreadId = buildCowtailTarget(params.threadId);
|
|
15509
|
+
if (prefixedThreadId === params.threadId) {
|
|
15510
|
+
return rawRoute;
|
|
15511
|
+
}
|
|
15512
|
+
const prefixedRoute = resolveAgentRouteForPeer({
|
|
15513
|
+
cfg: params.cfg,
|
|
15514
|
+
accountId: params.accountId,
|
|
15515
|
+
runtime: params.runtime,
|
|
15516
|
+
peerId: prefixedThreadId
|
|
15517
|
+
});
|
|
15518
|
+
return isPeerBindingRoute(prefixedRoute) ? prefixedRoute : rawRoute;
|
|
15519
|
+
}
|
|
15520
|
+
function resolveAgentRouteForPeer(params) {
|
|
15521
|
+
return params.runtime.channel.routing.resolveAgentRoute({
|
|
15522
|
+
cfg: params.cfg,
|
|
15523
|
+
channel: CHANNEL_ID,
|
|
15524
|
+
accountId: params.accountId,
|
|
15525
|
+
peer: {
|
|
15526
|
+
kind: "direct",
|
|
15527
|
+
id: params.peerId
|
|
15528
|
+
}
|
|
15529
|
+
});
|
|
15530
|
+
}
|
|
15531
|
+
function isPeerBindingRoute(route) {
|
|
15532
|
+
return route.matchedBy === "binding.peer";
|
|
15533
|
+
}
|
|
15534
|
+
function resolveCowtailThreadId(params) {
|
|
15535
|
+
return resolveCowtailThreadIdFromSessionKey(params.route.sessionKey) ?? normalizeCowtailTarget(params.thread.id);
|
|
15536
|
+
}
|
|
15422
15537
|
function errorMessage(error48) {
|
|
15423
15538
|
return error48 instanceof Error ? error48.message : String(error48);
|
|
15424
15539
|
}
|
|
@@ -15436,23 +15551,29 @@ function resolveCowtailTarget(to) {
|
|
|
15436
15551
|
if (!normalizedTarget) {
|
|
15437
15552
|
throw new Error("Cowtail target must not be blank");
|
|
15438
15553
|
}
|
|
15439
|
-
return
|
|
15554
|
+
return normalizedTarget;
|
|
15440
15555
|
}
|
|
15441
15556
|
async function sendCowtailText(params) {
|
|
15442
15557
|
const { client, to } = params;
|
|
15443
15558
|
const text = ensureNonBlankText(params.text);
|
|
15444
15559
|
const target = resolveCowtailTarget(to);
|
|
15560
|
+
const sessionKey = buildCowtailTarget(target);
|
|
15445
15561
|
const result = await client.sendOpenClawMessage({
|
|
15446
15562
|
type: "openclaw_message",
|
|
15447
|
-
sessionKey
|
|
15563
|
+
sessionKey,
|
|
15564
|
+
threadHint: target,
|
|
15448
15565
|
text,
|
|
15449
15566
|
links: [],
|
|
15450
15567
|
actions: []
|
|
15451
15568
|
});
|
|
15569
|
+
const messageId = result.payload?.messageId;
|
|
15570
|
+
if (typeof messageId !== "string" || messageId.trim() === "") {
|
|
15571
|
+
throw new Error("Cowtail did not acknowledge a durable message id");
|
|
15572
|
+
}
|
|
15452
15573
|
return {
|
|
15453
15574
|
channel: "cowtail",
|
|
15454
|
-
messageId
|
|
15455
|
-
to: target
|
|
15575
|
+
messageId,
|
|
15576
|
+
to: buildCowtailTarget(target)
|
|
15456
15577
|
};
|
|
15457
15578
|
}
|
|
15458
15579
|
|
package/dist/setup-entry.js
CHANGED
|
@@ -13801,6 +13801,7 @@ var openclawThreadRecordSchema = exports_external.object({
|
|
|
13801
13801
|
var openclawMessageRecordBaseSchema = exports_external.object({
|
|
13802
13802
|
id: nonEmptyStringSchema,
|
|
13803
13803
|
threadId: nonEmptyStringSchema,
|
|
13804
|
+
streamId: nonEmptyStringSchema.optional(),
|
|
13804
13805
|
direction: openclawMessageDirectionSchema,
|
|
13805
13806
|
authorLabel: nonEmptyStringSchema.optional(),
|
|
13806
13807
|
text: openclawMessageTextSchema,
|
|
@@ -13855,13 +13856,16 @@ var openclawPluginMessageCommandSchema = requireOpenClawRenderableContent(export
|
|
|
13855
13856
|
requestId: openclawRequestIdSchema,
|
|
13856
13857
|
idempotencyKey: openclawIdempotencyKeySchema,
|
|
13857
13858
|
sessionKey: nonEmptyStringSchema,
|
|
13859
|
+
threadId: nonEmptyStringSchema.optional(),
|
|
13860
|
+
threadHint: nonEmptyStringSchema.optional(),
|
|
13858
13861
|
title: nonEmptyStringSchema.optional(),
|
|
13859
13862
|
text: openclawMessageTextSchema,
|
|
13860
13863
|
authorLabel: nonEmptyStringSchema.optional(),
|
|
13861
13864
|
links: exports_external.array(openclawLinkSchema).default([]),
|
|
13862
13865
|
toolCalls: exports_external.array(openclawToolCallRecordSchema).default([]),
|
|
13863
13866
|
actions: exports_external.array(openclawActionDraftSchema).default([]),
|
|
13864
|
-
deliveryState: openclawDeliveryStateSchema.optional()
|
|
13867
|
+
deliveryState: openclawDeliveryStateSchema.optional(),
|
|
13868
|
+
streamId: nonEmptyStringSchema
|
|
13865
13869
|
}));
|
|
13866
13870
|
var openclawPluginMessageUpdateCommandSchema = requireOpenClawRenderableContent(exports_external.object({
|
|
13867
13871
|
type: exports_external.literal("openclaw_message_update"),
|
|
@@ -13872,7 +13876,8 @@ var openclawPluginMessageUpdateCommandSchema = requireOpenClawRenderableContent(
|
|
|
13872
13876
|
links: exports_external.array(openclawLinkSchema).optional(),
|
|
13873
13877
|
toolCalls: exports_external.array(openclawToolCallRecordSchema).optional(),
|
|
13874
13878
|
actions: exports_external.array(openclawActionDraftSchema).optional(),
|
|
13875
|
-
deliveryState: openclawDeliveryStateSchema.optional()
|
|
13879
|
+
deliveryState: openclawDeliveryStateSchema.optional(),
|
|
13880
|
+
streamId: nonEmptyStringSchema
|
|
13876
13881
|
}));
|
|
13877
13882
|
var openclawMessageStreamSnapshotCommandBaseSchema = exports_external.object({
|
|
13878
13883
|
type: exports_external.literal("openclaw_message_stream_snapshot"),
|
|
@@ -13884,6 +13889,7 @@ var openclawMessageStreamSnapshotCommandBaseSchema = exports_external.object({
|
|
|
13884
13889
|
links: exports_external.array(openclawLinkSchema).default([]),
|
|
13885
13890
|
toolCalls: exports_external.array(openclawToolCallRecordSchema).default([]),
|
|
13886
13891
|
isFinal: exports_external.boolean(),
|
|
13892
|
+
snapshotSequence: exports_external.number().int().nonnegative(),
|
|
13887
13893
|
updatedAt: timestampSchema
|
|
13888
13894
|
});
|
|
13889
13895
|
var openclawMessageStreamSnapshotCommandSchema = requireOpenClawRenderableContent(openclawMessageStreamSnapshotCommandBaseSchema);
|
|
@@ -13956,11 +13962,18 @@ var openclawRealtimeClientMessageSchema = exports_external.union([
|
|
|
13956
13962
|
openclawSessionBoundCommandSchema,
|
|
13957
13963
|
openclawActionResultCommandSchema
|
|
13958
13964
|
]);
|
|
13965
|
+
var openclawRealtimeAckPayloadSchema = exports_external.object({
|
|
13966
|
+
threadId: nonEmptyStringSchema.optional(),
|
|
13967
|
+
messageId: nonEmptyStringSchema.optional(),
|
|
13968
|
+
dropped: exports_external.literal(true).optional(),
|
|
13969
|
+
duplicate: exports_external.literal(true).optional(),
|
|
13970
|
+
reason: nonEmptyStringSchema.optional()
|
|
13971
|
+
});
|
|
13959
13972
|
var openclawRealtimeAckSchema = exports_external.object({
|
|
13960
13973
|
type: exports_external.literal("ack"),
|
|
13961
13974
|
requestId: openclawRequestIdSchema,
|
|
13962
13975
|
sequence: openclawSequenceSchema.optional(),
|
|
13963
|
-
payload:
|
|
13976
|
+
payload: openclawRealtimeAckPayloadSchema.optional()
|
|
13964
13977
|
});
|
|
13965
13978
|
var openclawRealtimeErrorSchema = exports_external.object({
|
|
13966
13979
|
type: exports_external.literal("realtime_error"),
|
|
@@ -14009,6 +14022,13 @@ var openclawEventReplayResponseSchema = exports_external.object({
|
|
|
14009
14022
|
ok: exports_external.literal(true),
|
|
14010
14023
|
events: exports_external.array(openclawEventEnvelopeSchema)
|
|
14011
14024
|
});
|
|
14025
|
+
var openclawPushNotificationPayloadSchema = exports_external.object({
|
|
14026
|
+
kind: exports_external.literal("openclaw"),
|
|
14027
|
+
version: exports_external.literal(1),
|
|
14028
|
+
threadId: nonEmptyStringSchema,
|
|
14029
|
+
messageId: nonEmptyStringSchema,
|
|
14030
|
+
url: nonEmptyStringSchema.optional()
|
|
14031
|
+
});
|
|
14012
14032
|
// ../protocol/src/fixes.ts
|
|
14013
14033
|
var fixScopeSchema = exports_external.enum(fixScopes);
|
|
14014
14034
|
var fixCreateRequestSchema = exports_external.object({
|
|
@@ -14064,11 +14084,12 @@ var healthResponseSchema = exports_external.object({
|
|
|
14064
14084
|
storageUnit: nonEmptyStringSchema
|
|
14065
14085
|
});
|
|
14066
14086
|
// ../protocol/src/push.ts
|
|
14087
|
+
var pushEnvironmentSchema = exports_external.enum(["development", "production"]);
|
|
14067
14088
|
var pushRegisterRequestSchema = exports_external.object({
|
|
14068
14089
|
identityToken: nonEmptyStringSchema,
|
|
14069
14090
|
deviceToken: nonEmptyStringSchema,
|
|
14070
14091
|
platform: nonEmptyStringSchema.optional(),
|
|
14071
|
-
environment:
|
|
14092
|
+
environment: pushEnvironmentSchema.optional(),
|
|
14072
14093
|
deviceName: nonEmptyStringSchema.optional()
|
|
14073
14094
|
});
|
|
14074
14095
|
var pushRegisterResponseSchema = exports_external.object({
|
|
@@ -14077,6 +14098,7 @@ var pushRegisterResponseSchema = exports_external.object({
|
|
|
14077
14098
|
id: nonEmptyStringSchema
|
|
14078
14099
|
});
|
|
14079
14100
|
var pushUnregisterRequestSchema = exports_external.object({
|
|
14101
|
+
identityToken: nonEmptyStringSchema,
|
|
14080
14102
|
deviceToken: nonEmptyStringSchema
|
|
14081
14103
|
});
|
|
14082
14104
|
var pushUnregisterResponseSchema = exports_external.object({
|
|
@@ -14100,6 +14122,7 @@ var pushResultSchema = exports_external.object({
|
|
|
14100
14122
|
userId: nonEmptyStringSchema,
|
|
14101
14123
|
sent: exports_external.number().int().nonnegative(),
|
|
14102
14124
|
failed: exports_external.number().int().nonnegative(),
|
|
14125
|
+
skipped: exports_external.number().int().nonnegative().default(0),
|
|
14103
14126
|
results: exports_external.array(jsonObjectSchema)
|
|
14104
14127
|
});
|
|
14105
14128
|
// ../protocol/src/responses.ts
|
|
@@ -14120,7 +14143,8 @@ var usersListEntrySchema = exports_external.object({
|
|
|
14120
14143
|
enabledDeviceCount: exports_external.number().int().nonnegative()
|
|
14121
14144
|
});
|
|
14122
14145
|
var userDeviceSchema = exports_external.object({
|
|
14123
|
-
|
|
14146
|
+
id: nonEmptyStringSchema,
|
|
14147
|
+
deviceTokenPreview: nonEmptyStringSchema,
|
|
14124
14148
|
platform: nonEmptyStringSchema,
|
|
14125
14149
|
environment: nonEmptyStringSchema,
|
|
14126
14150
|
enabled: exports_external.boolean(),
|
|
@@ -14192,10 +14216,12 @@ class CowtailRealtimeClient {
|
|
|
14192
14216
|
}
|
|
14193
14217
|
sendOpenClawMessage(command) {
|
|
14194
14218
|
const requestId = this.#requestIdFactory();
|
|
14219
|
+
const idempotencyKey = command.idempotencyKey ?? `cowtail:request:${requestId}`;
|
|
14195
14220
|
return this.#sendCommand({
|
|
14196
14221
|
...command,
|
|
14197
14222
|
requestId,
|
|
14198
|
-
idempotencyKey
|
|
14223
|
+
idempotencyKey,
|
|
14224
|
+
streamId: command.streamId ?? idempotencyKey,
|
|
14199
14225
|
links: command.links ?? [],
|
|
14200
14226
|
toolCalls: command.toolCalls ?? [],
|
|
14201
14227
|
actions: command.actions ?? []
|
|
@@ -14519,6 +14545,30 @@ function normalizeCowtailTarget(raw) {
|
|
|
14519
14545
|
function buildCowtailTarget(threadId) {
|
|
14520
14546
|
return `cowtail:${normalizeCowtailTarget(threadId)}`;
|
|
14521
14547
|
}
|
|
14548
|
+
function resolveCowtailThreadIdFromSessionKey(sessionKey) {
|
|
14549
|
+
const raw = (sessionKey ?? "").trim();
|
|
14550
|
+
if (!raw) {
|
|
14551
|
+
return;
|
|
14552
|
+
}
|
|
14553
|
+
const parts = raw.split(":").filter(Boolean);
|
|
14554
|
+
if (parts.length < 4 || parts[0]?.toLowerCase() !== "agent") {
|
|
14555
|
+
return;
|
|
14556
|
+
}
|
|
14557
|
+
const cowtailIndex = parts.findIndex((part, index) => index >= 2 && part.toLowerCase() === "cowtail");
|
|
14558
|
+
if (cowtailIndex < 0) {
|
|
14559
|
+
if (parts[2]?.toLowerCase() !== "direct") {
|
|
14560
|
+
return;
|
|
14561
|
+
}
|
|
14562
|
+
const directTarget = normalizeCowtailTarget(parts.slice(3).join(":"));
|
|
14563
|
+
return directTarget || undefined;
|
|
14564
|
+
}
|
|
14565
|
+
const rest = parts.slice(cowtailIndex + 1);
|
|
14566
|
+
const first = rest[0]?.toLowerCase();
|
|
14567
|
+
const second = rest[1]?.toLowerCase();
|
|
14568
|
+
const target = first === "direct" || first === "group" || first === "channel" ? rest.slice(1).join(":") : second === "direct" || second === "group" || second === "channel" ? rest.slice(2).join(":") : rest.join(":");
|
|
14569
|
+
const normalized = normalizeCowtailTarget(target);
|
|
14570
|
+
return normalized || undefined;
|
|
14571
|
+
}
|
|
14522
14572
|
function isSupportedCowtailAgent(agentId) {
|
|
14523
14573
|
return (agentId ?? "main").trim() === "main";
|
|
14524
14574
|
}
|
|
@@ -14555,10 +14605,11 @@ async function handleCowtailEvent(params) {
|
|
|
14555
14605
|
return;
|
|
14556
14606
|
}
|
|
14557
14607
|
if (!thread.sessionKey) {
|
|
14608
|
+
const threadId = resolveCowtailThreadId({ route, thread });
|
|
14558
14609
|
await client.sendSessionBound({
|
|
14559
14610
|
type: "openclaw_session_bound",
|
|
14560
|
-
idempotencyKey: `cowtail:session-bound:${
|
|
14561
|
-
threadId
|
|
14611
|
+
idempotencyKey: `cowtail:session-bound:${threadId}`,
|
|
14612
|
+
threadId,
|
|
14562
14613
|
sessionKey: route.sessionKey
|
|
14563
14614
|
});
|
|
14564
14615
|
}
|
|
@@ -14668,6 +14719,7 @@ async function dispatchCowtailTextTurn(params) {
|
|
|
14668
14719
|
idempotencyKey: `cowtail:reply:${message.id}`,
|
|
14669
14720
|
streamId: `cowtail:stream:${message.id}`,
|
|
14670
14721
|
updateIndex: 0,
|
|
14722
|
+
snapshotSequence: 0,
|
|
14671
14723
|
toolFallbackIndex: 0,
|
|
14672
14724
|
toolFallbackIds: {},
|
|
14673
14725
|
text: "",
|
|
@@ -14741,6 +14793,7 @@ async function dispatchCowtailActionTurn(params) {
|
|
|
14741
14793
|
idempotencyKey: `cowtail:action:${action.id}`,
|
|
14742
14794
|
streamId: `cowtail:action-stream:${action.id}`,
|
|
14743
14795
|
updateIndex: 0,
|
|
14796
|
+
snapshotSequence: 0,
|
|
14744
14797
|
toolFallbackIndex: 0,
|
|
14745
14798
|
toolFallbackIds: {},
|
|
14746
14799
|
text: "",
|
|
@@ -14921,28 +14974,17 @@ async function deliverCowtailReply(params) {
|
|
|
14921
14974
|
updatedAt: Date.now()
|
|
14922
14975
|
});
|
|
14923
14976
|
if (!params.streamState.messageId) {
|
|
14924
|
-
|
|
14925
|
-
type: "openclaw_message",
|
|
14926
|
-
idempotencyKey: params.streamState.idempotencyKey,
|
|
14927
|
-
sessionKey: params.thread.sessionKey ?? params.route.sessionKey,
|
|
14928
|
-
title: params.thread.title,
|
|
14977
|
+
await createDurableCowtailReply(params, {
|
|
14929
14978
|
text: messageText,
|
|
14930
|
-
authorLabel: "OpenClaw",
|
|
14931
14979
|
links,
|
|
14932
|
-
toolCalls: params.streamState.toolCalls,
|
|
14933
|
-
actions: [],
|
|
14934
14980
|
deliveryState: "pending"
|
|
14935
14981
|
});
|
|
14936
|
-
const messageId2 = recordCreatedOpenClawMessageId(params.streamState, result2);
|
|
14937
|
-
if (!messageId2) {
|
|
14938
|
-
return;
|
|
14939
|
-
}
|
|
14940
|
-
params.streamState.messageId = messageId2;
|
|
14941
14982
|
return;
|
|
14942
14983
|
}
|
|
14943
14984
|
await params.client.sendOpenClawMessageUpdate({
|
|
14944
14985
|
type: "openclaw_message_update",
|
|
14945
14986
|
idempotencyKey: nextStreamUpdateIdempotencyKey(params.streamState),
|
|
14987
|
+
streamId: params.streamState.streamId,
|
|
14946
14988
|
messageId: params.streamState.messageId,
|
|
14947
14989
|
text: messageText,
|
|
14948
14990
|
links,
|
|
@@ -14967,28 +15009,17 @@ async function deliverCowtailReply(params) {
|
|
|
14967
15009
|
updatedAt: Date.now()
|
|
14968
15010
|
});
|
|
14969
15011
|
if (!params.streamState.messageId) {
|
|
14970
|
-
|
|
14971
|
-
type: "openclaw_message",
|
|
14972
|
-
idempotencyKey: params.streamState.idempotencyKey,
|
|
14973
|
-
sessionKey: params.thread.sessionKey ?? params.route.sessionKey,
|
|
14974
|
-
title: params.thread.title,
|
|
15012
|
+
await createDurableCowtailReply(params, {
|
|
14975
15013
|
text: params.streamState.text,
|
|
14976
|
-
authorLabel: "OpenClaw",
|
|
14977
15014
|
links,
|
|
14978
|
-
toolCalls: params.streamState.toolCalls,
|
|
14979
|
-
actions: [],
|
|
14980
15015
|
deliveryState: "pending"
|
|
14981
15016
|
});
|
|
14982
|
-
const messageId2 = recordCreatedOpenClawMessageId(params.streamState, result2);
|
|
14983
|
-
if (!messageId2) {
|
|
14984
|
-
return;
|
|
14985
|
-
}
|
|
14986
|
-
params.streamState.messageId = messageId2;
|
|
14987
15017
|
return;
|
|
14988
15018
|
}
|
|
14989
15019
|
await params.client.sendOpenClawMessageUpdate({
|
|
14990
15020
|
type: "openclaw_message_update",
|
|
14991
15021
|
idempotencyKey: nextStreamUpdateIdempotencyKey(params.streamState),
|
|
15022
|
+
streamId: params.streamState.streamId,
|
|
14992
15023
|
messageId: params.streamState.messageId,
|
|
14993
15024
|
text: params.streamState.text,
|
|
14994
15025
|
links,
|
|
@@ -15002,6 +15033,7 @@ async function deliverCowtailReply(params) {
|
|
|
15002
15033
|
await params.client.sendOpenClawMessageUpdate({
|
|
15003
15034
|
type: "openclaw_message_update",
|
|
15004
15035
|
idempotencyKey: nextStreamUpdateIdempotencyKey(params.streamState),
|
|
15036
|
+
streamId: params.streamState.streamId,
|
|
15005
15037
|
messageId: params.streamState.messageId,
|
|
15006
15038
|
text,
|
|
15007
15039
|
links,
|
|
@@ -15023,23 +15055,14 @@ async function deliverCowtailReply(params) {
|
|
|
15023
15055
|
return;
|
|
15024
15056
|
}
|
|
15025
15057
|
params.streamState.text = text;
|
|
15026
|
-
const
|
|
15027
|
-
type: "openclaw_message",
|
|
15028
|
-
idempotencyKey: params.streamState.idempotencyKey,
|
|
15029
|
-
sessionKey: params.thread.sessionKey ?? params.route.sessionKey,
|
|
15030
|
-
title: params.thread.title,
|
|
15058
|
+
const messageId = await createDurableCowtailReply(params, {
|
|
15031
15059
|
text,
|
|
15032
|
-
authorLabel: "OpenClaw",
|
|
15033
15060
|
links,
|
|
15034
|
-
toolCalls: params.streamState.toolCalls,
|
|
15035
|
-
actions: [],
|
|
15036
15061
|
deliveryState: "sent"
|
|
15037
15062
|
});
|
|
15038
|
-
const messageId = recordCreatedOpenClawMessageId(params.streamState, result);
|
|
15039
15063
|
if (!messageId) {
|
|
15040
15064
|
return;
|
|
15041
15065
|
}
|
|
15042
|
-
params.streamState.messageId = messageId;
|
|
15043
15066
|
emitCowtailStreamSnapshot({
|
|
15044
15067
|
client: params.client,
|
|
15045
15068
|
logger: params.logger,
|
|
@@ -15052,6 +15075,35 @@ async function deliverCowtailReply(params) {
|
|
|
15052
15075
|
});
|
|
15053
15076
|
params.streamState.completed = true;
|
|
15054
15077
|
}
|
|
15078
|
+
async function createDurableCowtailReply(params, message) {
|
|
15079
|
+
try {
|
|
15080
|
+
const routedThreadId = resolveCowtailThreadIdFromSessionKey(params.route.sessionKey);
|
|
15081
|
+
const result = await params.client.sendOpenClawMessage({
|
|
15082
|
+
type: "openclaw_message",
|
|
15083
|
+
idempotencyKey: params.streamState.idempotencyKey,
|
|
15084
|
+
streamId: params.streamState.streamId,
|
|
15085
|
+
sessionKey: params.route.sessionKey,
|
|
15086
|
+
...routedThreadId ? { threadId: routedThreadId } : {},
|
|
15087
|
+
title: params.thread.title,
|
|
15088
|
+
text: message.text,
|
|
15089
|
+
authorLabel: "OpenClaw",
|
|
15090
|
+
links: message.links,
|
|
15091
|
+
toolCalls: params.streamState.toolCalls,
|
|
15092
|
+
actions: [],
|
|
15093
|
+
deliveryState: message.deliveryState
|
|
15094
|
+
});
|
|
15095
|
+
const messageId = recordCreatedOpenClawMessageId(params.streamState, result);
|
|
15096
|
+
if (!messageId) {
|
|
15097
|
+
failCowtailStreamBeforeDurableCreate(params);
|
|
15098
|
+
return;
|
|
15099
|
+
}
|
|
15100
|
+
params.streamState.messageId = messageId;
|
|
15101
|
+
return messageId;
|
|
15102
|
+
} catch (error48) {
|
|
15103
|
+
failCowtailStreamBeforeDurableCreate(params);
|
|
15104
|
+
throw error48;
|
|
15105
|
+
}
|
|
15106
|
+
}
|
|
15055
15107
|
async function finalizeCowtailStreamedReply(params) {
|
|
15056
15108
|
if (params.streamState.failed || params.streamState.completed || !params.streamState.messageId || !params.streamState.text.trim() && params.streamState.toolCalls.length === 0) {
|
|
15057
15109
|
return;
|
|
@@ -15059,6 +15111,7 @@ async function finalizeCowtailStreamedReply(params) {
|
|
|
15059
15111
|
await params.client.sendOpenClawMessageUpdate({
|
|
15060
15112
|
type: "openclaw_message_update",
|
|
15061
15113
|
idempotencyKey: nextStreamUpdateIdempotencyKey(params.streamState),
|
|
15114
|
+
streamId: params.streamState.streamId,
|
|
15062
15115
|
messageId: params.streamState.messageId,
|
|
15063
15116
|
text: params.streamState.text,
|
|
15064
15117
|
links: params.streamState.links,
|
|
@@ -15140,26 +15193,46 @@ function deliverCowtailToolProgress(params) {
|
|
|
15140
15193
|
});
|
|
15141
15194
|
}
|
|
15142
15195
|
function emitCowtailStreamSnapshot(params) {
|
|
15143
|
-
if (params.streamState.failed || !params.text.trim() && params.streamState.links.length === 0 && params.streamState.toolCalls.length === 0) {
|
|
15196
|
+
if (params.streamState.failed && params.allowFailed !== true || !params.text.trim() && params.streamState.links.length === 0 && params.streamState.toolCalls.length === 0) {
|
|
15144
15197
|
return;
|
|
15145
15198
|
}
|
|
15146
15199
|
params.streamState.liveText = params.text;
|
|
15147
15200
|
params.streamState.lastSnapshotText = params.text;
|
|
15148
15201
|
params.streamState.lastSnapshotSentAt = params.updatedAt;
|
|
15202
|
+
params.streamState.snapshotSequence += 1;
|
|
15149
15203
|
params.client.sendOpenClawStreamSnapshot({
|
|
15150
15204
|
type: "openclaw_message_stream_snapshot",
|
|
15151
15205
|
streamId: params.streamState.streamId,
|
|
15152
|
-
sessionKey: params.
|
|
15153
|
-
threadId: params
|
|
15206
|
+
sessionKey: params.route.sessionKey,
|
|
15207
|
+
threadId: resolveCowtailThreadId(params),
|
|
15154
15208
|
text: params.text,
|
|
15155
15209
|
links: params.streamState.links.map((link) => ({ ...link })),
|
|
15156
15210
|
toolCalls: structuredClone(params.streamState.toolCalls),
|
|
15157
15211
|
isFinal: params.isFinal,
|
|
15212
|
+
snapshotSequence: params.streamState.snapshotSequence,
|
|
15158
15213
|
updatedAt: params.updatedAt
|
|
15159
15214
|
}).catch((error48) => {
|
|
15160
15215
|
params.logger?.warn?.(`Cowtail stream snapshot send failed: ${errorMessage(error48)}`);
|
|
15161
15216
|
});
|
|
15162
15217
|
}
|
|
15218
|
+
function failCowtailStreamBeforeDurableCreate(params) {
|
|
15219
|
+
if (!params.streamState.messageId && params.streamState.snapshotSequence > 0) {
|
|
15220
|
+
const text = params.streamState.text.trim() ? params.streamState.text : currentLiveCowtailStreamText(params.streamState);
|
|
15221
|
+
emitCowtailStreamSnapshot({
|
|
15222
|
+
client: params.client,
|
|
15223
|
+
logger: params.logger,
|
|
15224
|
+
route: params.route,
|
|
15225
|
+
thread: params.thread,
|
|
15226
|
+
streamState: params.streamState,
|
|
15227
|
+
text,
|
|
15228
|
+
isFinal: true,
|
|
15229
|
+
updatedAt: Date.now(),
|
|
15230
|
+
allowFailed: true
|
|
15231
|
+
});
|
|
15232
|
+
}
|
|
15233
|
+
params.streamState.failed = true;
|
|
15234
|
+
params.streamState.completed = true;
|
|
15235
|
+
}
|
|
15163
15236
|
function currentLiveCowtailStreamText(streamState) {
|
|
15164
15237
|
return streamState.liveText ?? streamState.text;
|
|
15165
15238
|
}
|
|
@@ -15357,7 +15430,7 @@ function buildCowtailInboundBody(params) {
|
|
|
15357
15430
|
};
|
|
15358
15431
|
}
|
|
15359
15432
|
function finalizeInboundContext(params) {
|
|
15360
|
-
const target = buildCowtailTarget(params
|
|
15433
|
+
const target = buildCowtailTarget(resolveCowtailThreadId(params));
|
|
15361
15434
|
return params.runtime.channel.reply.finalizeInboundContext({
|
|
15362
15435
|
Body: params.body,
|
|
15363
15436
|
BodyForAgent: params.text,
|
|
@@ -15401,14 +15474,17 @@ function resolveThreadRoute(params) {
|
|
|
15401
15474
|
sessionKey: params.thread.sessionKey
|
|
15402
15475
|
};
|
|
15403
15476
|
}
|
|
15404
|
-
const
|
|
15405
|
-
|
|
15406
|
-
|
|
15477
|
+
const threadId = normalizeCowtailTarget(params.thread.id);
|
|
15478
|
+
if (!threadId) {
|
|
15479
|
+
params.logger?.warn?.("Cowtail thread_created event has a blank thread id");
|
|
15480
|
+
return null;
|
|
15481
|
+
}
|
|
15482
|
+
const cfg = params.runtime.config.loadConfig();
|
|
15483
|
+
const route = resolveCowtailAgentRoute({
|
|
15484
|
+
cfg,
|
|
15485
|
+
threadId,
|
|
15407
15486
|
accountId: params.account.accountId,
|
|
15408
|
-
|
|
15409
|
-
kind: "direct",
|
|
15410
|
-
id: buildCowtailTarget(params.thread.id)
|
|
15411
|
-
}
|
|
15487
|
+
runtime: params.runtime
|
|
15412
15488
|
});
|
|
15413
15489
|
if (!isSupportedCowtailAgent(route.agentId)) {
|
|
15414
15490
|
params.logger?.warn?.(`Cowtail thread ${params.thread.id} resolved unsupported agent ${route.agentId}`);
|
|
@@ -15419,6 +15495,45 @@ function resolveThreadRoute(params) {
|
|
|
15419
15495
|
sessionKey: route.sessionKey
|
|
15420
15496
|
};
|
|
15421
15497
|
}
|
|
15498
|
+
function resolveCowtailAgentRoute(params) {
|
|
15499
|
+
const rawRoute = resolveAgentRouteForPeer({
|
|
15500
|
+
cfg: params.cfg,
|
|
15501
|
+
accountId: params.accountId,
|
|
15502
|
+
runtime: params.runtime,
|
|
15503
|
+
peerId: params.threadId
|
|
15504
|
+
});
|
|
15505
|
+
if (isPeerBindingRoute(rawRoute)) {
|
|
15506
|
+
return rawRoute;
|
|
15507
|
+
}
|
|
15508
|
+
const prefixedThreadId = buildCowtailTarget(params.threadId);
|
|
15509
|
+
if (prefixedThreadId === params.threadId) {
|
|
15510
|
+
return rawRoute;
|
|
15511
|
+
}
|
|
15512
|
+
const prefixedRoute = resolveAgentRouteForPeer({
|
|
15513
|
+
cfg: params.cfg,
|
|
15514
|
+
accountId: params.accountId,
|
|
15515
|
+
runtime: params.runtime,
|
|
15516
|
+
peerId: prefixedThreadId
|
|
15517
|
+
});
|
|
15518
|
+
return isPeerBindingRoute(prefixedRoute) ? prefixedRoute : rawRoute;
|
|
15519
|
+
}
|
|
15520
|
+
function resolveAgentRouteForPeer(params) {
|
|
15521
|
+
return params.runtime.channel.routing.resolveAgentRoute({
|
|
15522
|
+
cfg: params.cfg,
|
|
15523
|
+
channel: CHANNEL_ID,
|
|
15524
|
+
accountId: params.accountId,
|
|
15525
|
+
peer: {
|
|
15526
|
+
kind: "direct",
|
|
15527
|
+
id: params.peerId
|
|
15528
|
+
}
|
|
15529
|
+
});
|
|
15530
|
+
}
|
|
15531
|
+
function isPeerBindingRoute(route) {
|
|
15532
|
+
return route.matchedBy === "binding.peer";
|
|
15533
|
+
}
|
|
15534
|
+
function resolveCowtailThreadId(params) {
|
|
15535
|
+
return resolveCowtailThreadIdFromSessionKey(params.route.sessionKey) ?? normalizeCowtailTarget(params.thread.id);
|
|
15536
|
+
}
|
|
15422
15537
|
function errorMessage(error48) {
|
|
15423
15538
|
return error48 instanceof Error ? error48.message : String(error48);
|
|
15424
15539
|
}
|
|
@@ -15436,23 +15551,29 @@ function resolveCowtailTarget(to) {
|
|
|
15436
15551
|
if (!normalizedTarget) {
|
|
15437
15552
|
throw new Error("Cowtail target must not be blank");
|
|
15438
15553
|
}
|
|
15439
|
-
return
|
|
15554
|
+
return normalizedTarget;
|
|
15440
15555
|
}
|
|
15441
15556
|
async function sendCowtailText(params) {
|
|
15442
15557
|
const { client, to } = params;
|
|
15443
15558
|
const text = ensureNonBlankText(params.text);
|
|
15444
15559
|
const target = resolveCowtailTarget(to);
|
|
15560
|
+
const sessionKey = buildCowtailTarget(target);
|
|
15445
15561
|
const result = await client.sendOpenClawMessage({
|
|
15446
15562
|
type: "openclaw_message",
|
|
15447
|
-
sessionKey
|
|
15563
|
+
sessionKey,
|
|
15564
|
+
threadHint: target,
|
|
15448
15565
|
text,
|
|
15449
15566
|
links: [],
|
|
15450
15567
|
actions: []
|
|
15451
15568
|
});
|
|
15569
|
+
const messageId = result.payload?.messageId;
|
|
15570
|
+
if (typeof messageId !== "string" || messageId.trim() === "") {
|
|
15571
|
+
throw new Error("Cowtail did not acknowledge a durable message id");
|
|
15572
|
+
}
|
|
15452
15573
|
return {
|
|
15453
15574
|
channel: "cowtail",
|
|
15454
|
-
messageId
|
|
15455
|
-
to: target
|
|
15575
|
+
messageId,
|
|
15576
|
+
to: buildCowtailTarget(target)
|
|
15456
15577
|
};
|
|
15457
15578
|
}
|
|
15458
15579
|
|