@saleso.innovations/bridge 0.1.17 → 0.1.19

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 CHANGED
@@ -34,7 +34,7 @@ This installs the latest package, refreshes the CLI symlink, and restarts `cleos
34
34
  To pin a specific release (e.g. after a Cleos update):
35
35
 
36
36
  ```bash
37
- curl -fsSL https://amicable-elephant-407.convex.site/update-bridge.sh | bash -s -- 0.1.17
37
+ curl -fsSL https://amicable-elephant-407.convex.site/update-bridge.sh | bash -s -- 0.1.19
38
38
  ```
39
39
 
40
40
  ## Manual usage
@@ -1,4 +1,6 @@
1
1
  import { type SavedAgentCredentials } from "./credentials.js";
2
- export declare function resolveActiveConversationId(credentials?: SavedAgentCredentials): Promise<string>;
2
+ export declare function resolveActiveConversationId(credentials?: SavedAgentCredentials, options?: {
3
+ forceRefresh?: boolean;
4
+ }): Promise<string>;
3
5
  export declare function rememberConversationId(conversationId: string): void;
4
6
  //# sourceMappingURL=activeConversation.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"activeConversation.d.ts","sourceRoot":"","sources":["../src/activeConversation.ts"],"names":[],"mappings":"AAEA,OAAO,EAAqC,KAAK,qBAAqB,EAAE,MAAM,kBAAkB,CAAC;AAEjG,wBAAsB,2BAA2B,CAC/C,WAAW,CAAC,EAAE,qBAAqB,GAClC,OAAO,CAAC,MAAM,CAAC,CA+BjB;AAED,wBAAgB,sBAAsB,CAAC,cAAc,EAAE,MAAM,GAAG,IAAI,CAEnE"}
1
+ {"version":3,"file":"activeConversation.d.ts","sourceRoot":"","sources":["../src/activeConversation.ts"],"names":[],"mappings":"AAEA,OAAO,EAAqC,KAAK,qBAAqB,EAAE,MAAM,kBAAkB,CAAC;AAEjG,wBAAsB,2BAA2B,CAC/C,WAAW,CAAC,EAAE,qBAAqB,EACnC,OAAO,GAAE;IAAE,YAAY,CAAC,EAAE,OAAO,CAAA;CAAO,GACvC,OAAO,CAAC,MAAM,CAAC,CA+BjB;AAED,wBAAgB,sBAAsB,CAAC,cAAc,EAAE,MAAM,GAAG,IAAI,CAEnE"}
@@ -1,12 +1,12 @@
1
1
  import { convexSiteUrlFromEnv } from "./resolve.js";
2
2
  import { hashToken } from "./token.js";
3
3
  import { loadCredentials, patchCredentials } from "./credentials.js";
4
- export async function resolveActiveConversationId(credentials) {
4
+ export async function resolveActiveConversationId(credentials, options = {}) {
5
5
  const resolved = credentials ?? loadCredentials();
6
6
  if (!resolved) {
7
7
  throw new Error("No saved Cleos agent credentials");
8
8
  }
9
- if (resolved.conversationId) {
9
+ if (resolved.conversationId && !options.forceRefresh) {
10
10
  return resolved.conversationId;
11
11
  }
12
12
  const convexSiteUrl = resolved.convexSiteUrl ?? convexSiteUrlFromEnv();
package/dist/cli.js CHANGED
@@ -81,6 +81,21 @@ async function main() {
81
81
  }
82
82
  const session = await reconnectHermesAgent({ onUserMessage });
83
83
  try {
84
+ if (process.platform !== "win32") {
85
+ try {
86
+ const { execSync } = await import("node:child_process");
87
+ const active = execSync("systemctl is-active cleos-bridge.service", {
88
+ encoding: "utf8",
89
+ stdio: ["ignore", "pipe", "ignore"],
90
+ }).trim();
91
+ if (active === "active") {
92
+ console.error("Warning: cleos-bridge.service is running. Stop it first to avoid relay disconnects:\n systemctl stop cleos-bridge.service");
93
+ }
94
+ }
95
+ catch {
96
+ // systemctl unavailable or service not installed.
97
+ }
98
+ }
84
99
  const summary = await backfillCronDeliveries(session, { reset });
85
100
  console.log(JSON.stringify({ event: "cleos-bridge.cron-backfill.complete", ...summary }, null, 2));
86
101
  if (summary.remainingCount > 0) {
package/dist/client.d.ts CHANGED
@@ -37,7 +37,7 @@ export type AgentFailedPayload = {
37
37
  export type AgentReply = {
38
38
  delta: (text: string, sequence: number) => void;
39
39
  activity: (activity: AgentActivityPayload) => void;
40
- complete: (text: string, sequence: number, attachments?: UserMessageAttachment[]) => void;
40
+ complete: (text: string, sequence: number, attachments?: UserMessageAttachment[], hermesAssistantMessageId?: string) => void;
41
41
  failed: (failure: AgentFailedPayload) => void;
42
42
  };
43
43
  export type CronDeliveryMeta = {
@@ -51,7 +51,7 @@ export type ConnectResult = {
51
51
  agentToken: string;
52
52
  close: () => void;
53
53
  closed: Promise<void>;
54
- deliverCronResult: (content: string, meta: CronDeliveryMeta) => void;
54
+ deliverCronResult: (content: string, meta: CronDeliveryMeta) => Promise<void>;
55
55
  };
56
56
  export declare function pairCleosAgent(options: Omit<ConnectOptions, "onUserMessage">): Promise<{
57
57
  agentId: string;
@@ -1 +1 @@
1
- {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"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"}
package/dist/client.js CHANGED
@@ -29,12 +29,13 @@ function parseUserMessageAttachments(raw) {
29
29
  }
30
30
  return attachments.length > 0 ? attachments : undefined;
31
31
  }
32
- function sendCronResult(ws, agentId, content, meta) {
32
+ const CRON_DELIVERY_TIMEOUT_MS = 45_000;
33
+ function sendCronResult(ws, agentId, messageId, content, meta) {
33
34
  ws.send(JSON.stringify({
34
35
  type: "agent.message",
35
36
  agentId,
36
37
  conversationId: meta.conversationId,
37
- messageId: randomUUID(),
38
+ messageId,
38
39
  content,
39
40
  sequence: 0,
40
41
  final: true,
@@ -72,7 +73,7 @@ function createReplySender(ws, agentId, conversationId, messageId) {
72
73
  sequence: activity.sequence,
73
74
  }));
74
75
  },
75
- complete(text, sequence, attachments) {
76
+ complete(text, sequence, attachments, hermesAssistantMessageId) {
76
77
  ws.send(JSON.stringify({
77
78
  type: "agent.message",
78
79
  agentId,
@@ -82,6 +83,9 @@ function createReplySender(ws, agentId, conversationId, messageId) {
82
83
  sequence,
83
84
  final: true,
84
85
  ...(attachments && attachments.length > 0 ? { attachments } : {}),
86
+ ...(hermesAssistantMessageId
87
+ ? { metadata: { hermes: { assistantMessageId: hermesAssistantMessageId } } }
88
+ : {}),
85
89
  }));
86
90
  },
87
91
  failed(failure) {
@@ -166,6 +170,7 @@ function userSafeHermesError(error) {
166
170
  }
167
171
  async function openAgentConnection(options) {
168
172
  const ws = new WebSocket(options.relayWsUrl);
173
+ const pendingAcks = new Map();
169
174
  let closedResolve = null;
170
175
  const closed = new Promise((resolve) => {
171
176
  closedResolve = resolve;
@@ -183,6 +188,16 @@ async function openAgentConnection(options) {
183
188
  ws.on("message", (raw) => {
184
189
  void (async () => {
185
190
  const envelope = JSON.parse(raw.toString());
191
+ if (envelope.type === "ack") {
192
+ const requestId = typeof envelope.requestId === "string" ? envelope.requestId : "";
193
+ const resolveAck = requestId ? pendingAcks.get(requestId) : undefined;
194
+ if (resolveAck) {
195
+ pendingAcks.delete(requestId);
196
+ const message = typeof envelope.message === "string" ? envelope.message : undefined;
197
+ resolveAck(envelope.ok === true, message);
198
+ }
199
+ return;
200
+ }
186
201
  if (envelope.type === "hermes.command") {
187
202
  await handleHermesCommandEnvelope(ws, envelope, options.agentId);
188
203
  return;
@@ -224,6 +239,10 @@ async function openAgentConnection(options) {
224
239
  }, 30_000);
225
240
  ws.on("close", () => {
226
241
  clearInterval(heartbeat);
242
+ for (const [requestId, resolveAck] of pendingAcks.entries()) {
243
+ pendingAcks.delete(requestId);
244
+ resolveAck(false);
245
+ }
227
246
  closedResolve?.();
228
247
  });
229
248
  return {
@@ -234,11 +253,26 @@ async function openAgentConnection(options) {
234
253
  ws.close();
235
254
  },
236
255
  closed,
237
- deliverCronResult(content, meta) {
256
+ async deliverCronResult(content, meta) {
238
257
  if (ws.readyState !== WebSocket.OPEN) {
239
258
  throw new Error("Cleos relay connection is not open");
240
259
  }
241
- sendCronResult(ws, options.agentId, content, meta);
260
+ const messageId = randomUUID();
261
+ await new Promise((resolve, reject) => {
262
+ const timer = setTimeout(() => {
263
+ pendingAcks.delete(messageId);
264
+ reject(new Error("Cron delivery ack timed out"));
265
+ }, CRON_DELIVERY_TIMEOUT_MS);
266
+ pendingAcks.set(messageId, (ok, failureMessage) => {
267
+ clearTimeout(timer);
268
+ if (ok) {
269
+ resolve();
270
+ return;
271
+ }
272
+ reject(new Error(failureMessage ?? "Cron delivery persist failed"));
273
+ });
274
+ sendCronResult(ws, options.agentId, messageId, content, meta);
275
+ });
242
276
  },
243
277
  };
244
278
  }
@@ -1 +1 @@
1
- {"version":3,"file":"cronWatcher.d.ts","sourceRoot":"","sources":["../src/cronWatcher.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAIjD,eAAO,MAAM,sBAAsB,QAA+C,CAAC;AAEnF,eAAO,MAAM,oBAAoB,QAAmD,CAAC;AASrF,KAAK,kBAAkB,GAAG;IACxB,OAAO,EAAE,aAAa,CAAC;IACvB,WAAW,CAAC,EAAE,CAAC,IAAI,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAA;KAAE,KAAK,IAAI,CAAC;IACnF,OAAO,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;CACrC,CAAC;AAEF,wBAAgB,kBAAkB,IAAI,GAAG,CAAC,MAAM,CAAC,CAQhD;AAED,wBAAgB,mBAAmB,CAAC,SAAS,EAAE,GAAG,CAAC,MAAM,CAAC,GAAG,IAAI,CAGhE;AAED,wBAAgB,mBAAmB,IAAI,MAAM,CAI5C;AAED,wBAAgB,mBAAmB,CAAC,OAAO,GAAE,MAA+B,GAAG,MAAM,EAAE,CAoBtF;AAED,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAE1D;AAED,wBAAgB,oBAAoB,CAAC,SAAS,GAAE,GAAG,CAAC,MAAM,CAAwB,GAAG,MAAM,EAAE,CAE5F;AAqHD,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,kBAAkB,GAAG,MAAM,IAAI,CA8BxE;AAED,wBAAsB,eAAe,CAAC,OAAO,EAAE,aAAa,EAAE,UAAU,EAAE,OAAO,GAAG,OAAO,CAAC;IAC1F,cAAc,EAAE,MAAM,CAAC;IACvB,aAAa,EAAE,MAAM,CAAC;IACtB,cAAc,EAAE,MAAM,CAAC;IACvB,cAAc,EAAE,MAAM,CAAC;CACxB,CAAC,CAsBD"}
1
+ {"version":3,"file":"cronWatcher.d.ts","sourceRoot":"","sources":["../src/cronWatcher.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAIjD,eAAO,MAAM,sBAAsB,QAA+C,CAAC;AAEnF,eAAO,MAAM,oBAAoB,QAAmD,CAAC;AASrF,KAAK,kBAAkB,GAAG;IACxB,OAAO,EAAE,aAAa,CAAC;IACvB,WAAW,CAAC,EAAE,CAAC,IAAI,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAA;KAAE,KAAK,IAAI,CAAC;IACnF,OAAO,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;CACrC,CAAC;AAEF,wBAAgB,kBAAkB,IAAI,GAAG,CAAC,MAAM,CAAC,CAQhD;AAED,wBAAgB,mBAAmB,CAAC,SAAS,EAAE,GAAG,CAAC,MAAM,CAAC,GAAG,IAAI,CAGhE;AAED,wBAAgB,mBAAmB,IAAI,MAAM,CAI5C;AAED,wBAAgB,mBAAmB,CAAC,OAAO,GAAE,MAA+B,GAAG,MAAM,EAAE,CAoBtF;AAED,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAE1D;AAED,wBAAgB,oBAAoB,CAAC,SAAS,GAAE,GAAG,CAAC,MAAM,CAAwB,GAAG,MAAM,EAAE,CAE5F;AA+JD,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,kBAAkB,GAAG,MAAM,IAAI,CA4BxE;AAED,wBAAsB,eAAe,CAAC,OAAO,EAAE,aAAa,EAAE,UAAU,EAAE,OAAO,GAAG,OAAO,CAAC;IAC1F,cAAc,EAAE,MAAM,CAAC;IACvB,aAAa,EAAE,MAAM,CAAC;IACtB,cAAc,EAAE,MAAM,CAAC;IACvB,cAAc,EAAE,MAAM,CAAC;CACxB,CAAC,CAmBD"}
@@ -83,6 +83,13 @@ function parseRunAtFromFileName(filePath) {
83
83
  const base = filePath.split("/").pop()?.replace(/\.md$/i, "") ?? "";
84
84
  if (!base)
85
85
  return undefined;
86
+ const underscoreMatch = /^(\d{4}-\d{2}-\d{2})_(\d{2})-(\d{2})-(\d{2})$/.exec(base);
87
+ if (underscoreMatch) {
88
+ const [, datePart, hour, minute, second] = underscoreMatch;
89
+ const parsed = Date.parse(`${datePart}T${hour}:${minute}:${second}`);
90
+ if (Number.isFinite(parsed))
91
+ return parsed;
92
+ }
86
93
  const asNumber = Number(base);
87
94
  if (Number.isFinite(asNumber) && asNumber > 0)
88
95
  return asNumber;
@@ -114,7 +121,7 @@ function sleep(ms) {
114
121
  function isRelayNotOpenError(error) {
115
122
  return error instanceof Error && error.message === "Cleos relay connection is not open";
116
123
  }
117
- async function deliverPendingFiles(session, delivered, conversationId, options) {
124
+ async function deliverPendingFiles(session, delivered, conversationRef, options) {
118
125
  const jobNames = loadHermesCronJobs();
119
126
  for (const filePath of listCronOutputFiles()) {
120
127
  const key = relativeOutputKey(filePath);
@@ -129,24 +136,50 @@ async function deliverPendingFiles(session, delivered, conversationId, options)
129
136
  writeDeliveredIndex(delivered);
130
137
  continue;
131
138
  }
132
- try {
133
- session.deliverCronResult(content, {
134
- conversationId,
135
- jobId,
136
- jobName,
137
- runAt: parseRunAtFromFileName(filePath),
138
- });
139
- }
140
- catch (error) {
141
- if (isRelayNotOpenError(error)) {
142
- console.log(JSON.stringify({
143
- event: "cleos-bridge.cron-deferred",
139
+ let retriedAfterConversationRefresh = false;
140
+ let deliveredSuccessfully = false;
141
+ while (true) {
142
+ try {
143
+ await session.deliverCronResult(content, {
144
+ conversationId: conversationRef.value,
145
+ jobId,
146
+ jobName,
147
+ runAt: parseRunAtFromFileName(filePath),
148
+ });
149
+ deliveredSuccessfully = true;
150
+ break;
151
+ }
152
+ catch (error) {
153
+ if (isRelayNotOpenError(error)) {
154
+ console.log(JSON.stringify({
155
+ event: "cleos-bridge.cron-deferred",
156
+ file: key,
157
+ reason: "relay_not_open",
158
+ }));
159
+ return;
160
+ }
161
+ const message = error instanceof Error ? error.message : String(error);
162
+ const shouldRefreshConversation = !retriedAfterConversationRefresh
163
+ && (message.includes("Invalid relay message target")
164
+ || message.includes("Active conversation")
165
+ || message.includes("Cron delivery persist failed"));
166
+ if (shouldRefreshConversation) {
167
+ conversationRef.value = await resolveActiveConversationId(undefined, { forceRefresh: true });
168
+ retriedAfterConversationRefresh = true;
169
+ continue;
170
+ }
171
+ options.onError?.(message);
172
+ console.error(JSON.stringify({
173
+ event: "cleos-bridge.cron-delivery-failed",
144
174
  file: key,
145
- reason: "relay_not_open",
175
+ message,
176
+ conversationId: conversationRef.value,
146
177
  }));
147
- return;
178
+ break;
148
179
  }
149
- throw error;
180
+ }
181
+ if (!deliveredSuccessfully) {
182
+ continue;
150
183
  }
151
184
  delivered.add(key);
152
185
  writeDeliveredIndex(delivered);
@@ -168,10 +201,8 @@ export function startCronWatcher(options) {
168
201
  if (stopped)
169
202
  return;
170
203
  try {
171
- if (!conversationId) {
172
- conversationId = await resolveActiveConversationId();
173
- }
174
- await deliverPendingFiles(options.session, delivered, conversationId, options);
204
+ conversationId = await resolveActiveConversationId(undefined, { forceRefresh: !conversationId });
205
+ await deliverPendingFiles(options.session, delivered, { value: conversationId }, options);
175
206
  }
176
207
  catch (error) {
177
208
  const message = error instanceof Error ? error.message : String(error);
@@ -192,12 +223,9 @@ export async function runCronBackfill(session, resetIndex) {
192
223
  const clearedEntries = resetIndex ? clearDeliveredIndex() : 0;
193
224
  const delivered = readDeliveredIndex();
194
225
  const pendingAfterReset = listPendingCronFiles(delivered).length;
195
- let conversationId = loadCredentials()?.conversationId ?? null;
196
- if (!conversationId) {
197
- conversationId = await resolveActiveConversationId();
198
- }
226
+ let conversationId = await resolveActiveConversationId(undefined, { forceRefresh: true });
199
227
  const deliveredBefore = delivered.size;
200
- await deliverPendingFiles(session, delivered, conversationId, {});
228
+ await deliverPendingFiles(session, delivered, { value: conversationId }, {});
201
229
  const deliveredCount = delivered.size - deliveredBefore;
202
230
  const remainingCount = listPendingCronFiles(delivered).length;
203
231
  return {
@@ -1 +1 @@
1
- {"version":3,"file":"hermesForwarder.d.ts","sourceRoot":"","sources":["../src/hermesForwarder.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,UAAU,EAAyB,eAAe,EAAE,MAAM,aAAa,CAAC;AAWtF,MAAM,MAAM,sBAAsB,GAAG;IACnC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB,CAAC;AAEF,MAAM,MAAM,mBAAmB,GAAG;IAChC,SAAS,EAAE,MAAM,CAAC;CACnB,CAAC;AAkBF,wBAAgB,sBAAsB,CAAC,OAAO,GAAE,sBAA2B,GAAG;IAC5E,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC;IAC3B,KAAK,EAAE,MAAM,CAAC;CACf,CAUA;AA+MD,wBAAsB,eAAe,CACnC,OAAO,EAAE,MAAM,EACf,IAAI,EAAE,eAAe,EACrB,KAAK,EAAE,UAAU,EACjB,OAAO,GAAE,sBAA2B,GACnC,OAAO,CAAC,mBAAmB,CAAC,CAuE9B;AAED,wBAAgB,0BAA0B,CAAC,OAAO,GAAE,sBAA2B,IAC/D,SAAS,MAAM,EAAE,MAAM,eAAe,EAAE,OAAO,UAAU,KAAG,OAAO,CAAC,IAAI,CAAC,CAMxF"}
1
+ {"version":3,"file":"hermesForwarder.d.ts","sourceRoot":"","sources":["../src/hermesForwarder.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,UAAU,EAAyB,eAAe,EAAE,MAAM,aAAa,CAAC;AAWtF,MAAM,MAAM,sBAAsB,GAAG;IACnC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB,CAAC;AAEF,MAAM,MAAM,mBAAmB,GAAG;IAChC,SAAS,EAAE,MAAM,CAAC;CACnB,CAAC;AAkBF,wBAAgB,sBAAsB,CAAC,OAAO,GAAE,sBAA2B,GAAG;IAC5E,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC;IAC3B,KAAK,EAAE,MAAM,CAAC;CACf,CAUA;AA+MD,wBAAsB,eAAe,CACnC,OAAO,EAAE,MAAM,EACf,IAAI,EAAE,eAAe,EACrB,KAAK,EAAE,UAAU,EACjB,OAAO,GAAE,sBAA2B,GACnC,OAAO,CAAC,mBAAmB,CAAC,CA6E9B;AAED,wBAAgB,0BAA0B,CAAC,OAAO,GAAE,sBAA2B,IAC/D,SAAS,MAAM,EAAE,MAAM,eAAe,EAAE,OAAO,UAAU,KAAG,OAAO,CAAC,IAAI,CAAC,CAMxF"}
@@ -102,7 +102,7 @@ function processSseEventBlock(eventBlock, reply, counters, onTextDelta) {
102
102
  try {
103
103
  const payload = JSON.parse(data);
104
104
  const delta = extractDeltaFromChunk(payload);
105
- if (delta) {
105
+ if (delta && delta.trim()) {
106
106
  onTextDelta(delta);
107
107
  reply.delta(delta, counters.textSequence);
108
108
  counters.textSequence += 1;
@@ -262,7 +262,8 @@ export async function forwardToHermes(content, meta, reply, options = {}) {
262
262
  const mediaRefs = parseMediaReferences(fullText);
263
263
  const attachments = mediaRefs.length > 0 ? await uploadMediaAttachments(meta.agentId, mediaRefs) : [];
264
264
  const displayText = attachments.length > 0 ? stripMediaReferences(fullText, mediaRefs) : fullText.trim() || fullText;
265
- reply.complete(displayText, counters.textSequence, attachments.length > 0 ? attachments : undefined);
265
+ const { assistantMessageId } = getLatestTurnMessageIds(resolvedSessionId);
266
+ reply.complete(displayText, counters.textSequence, attachments.length > 0 ? attachments : undefined, assistantMessageId !== undefined ? String(assistantMessageId) : undefined);
266
267
  await linkTurnToConvex(meta, resolvedSessionId);
267
268
  return { sessionId: resolvedSessionId };
268
269
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@saleso.innovations/bridge",
3
- "version": "0.1.17",
3
+ "version": "0.1.19",
4
4
  "description": "Connect your Hermes agent to the Cleos iOS app via pairing code.",
5
5
  "type": "module",
6
6
  "license": "MIT",