dooers-agents-client 0.14.0 → 0.15.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/dist/main.cjs CHANGED
@@ -211,6 +211,20 @@ function toThreadEvent(w) {
211
211
  clientEventId: w.client_event_id
212
212
  };
213
213
  }
214
+ function toThreadArtifact(w) {
215
+ return {
216
+ eventId: w.event_id,
217
+ direction: w.direction,
218
+ kind: w.kind,
219
+ filename: w.filename,
220
+ mimeType: w.mime_type,
221
+ url: w.url,
222
+ refId: w.ref_id,
223
+ createdAt: w.created_at,
224
+ userId: w.user_id,
225
+ userName: w.user_name
226
+ };
227
+ }
214
228
  function toRun(w) {
215
229
  return {
216
230
  id: w.id,
@@ -308,12 +322,13 @@ function toWireContentPart(p) {
308
322
  }
309
323
 
310
324
  // src/version.ts
311
- var PACKAGE_VERSION = "0.14.0";
325
+ var PACKAGE_VERSION = "0.15.1";
312
326
 
313
327
  // src/client.ts
314
328
  var MAX_RECONNECT_ATTEMPTS = 5;
315
329
  var RECONNECT_DELAYS = [1e3, 2e3, 4e3, 8e3, 16e3];
316
330
  var SEND_MESSAGE_TIMEOUT = 3e4;
331
+ var ARTIFACTS_LIST_TIMEOUT = 5e3;
317
332
  var PING_INTERVAL_MS = 3e4;
318
333
  function blobPartFilename(blob, override) {
319
334
  const o = (override ?? "").trim();
@@ -467,6 +482,7 @@ var AgentClient = class {
467
482
  pendingOptimistic = /* @__PURE__ */ new Map();
468
483
  // Pending message promises — keyed by outbound frame id (same as client_event_id for sends).
469
484
  pendingMessages = /* @__PURE__ */ new Map();
485
+ pendingArtifactsRequests = /* @__PURE__ */ new Map();
470
486
  // Track last event ID per thread for gap recovery on reconnect
471
487
  lastEventIds = /* @__PURE__ */ new Map();
472
488
  // Pagination
@@ -604,6 +620,13 @@ var AgentClient = class {
604
620
  }
605
621
  this.pendingMessages.clear();
606
622
  }
623
+ abortPendingArtifacts(reason) {
624
+ for (const [frameId, pending] of this.pendingArtifactsRequests) {
625
+ clearTimeout(pending.timer);
626
+ this.pendingArtifactsRequests.delete(frameId);
627
+ pending.reject(new Error(reason));
628
+ }
629
+ }
607
630
  disconnect() {
608
631
  this.isIntentionallyClosed = true;
609
632
  this.stopHeartbeat();
@@ -612,6 +635,7 @@ var AgentClient = class {
612
635
  this.reconnectTimer = null;
613
636
  }
614
637
  this.abortPendingMessages("Disconnected");
638
+ this.abortPendingArtifacts("Disconnected");
615
639
  if (this.ws) {
616
640
  this.ws.onopen = null;
617
641
  this.ws.onmessage = null;
@@ -670,6 +694,40 @@ var AgentClient = class {
670
694
  limit
671
695
  });
672
696
  }
697
+ requestThreadArtifactsList(threadId, options) {
698
+ if (!this.isConnected()) {
699
+ return Promise.reject(new Error("Not connected"));
700
+ }
701
+ const frameId = crypto.randomUUID();
702
+ return new Promise((resolve, reject) => {
703
+ const timer = setTimeout(() => {
704
+ this.pendingArtifactsRequests.delete(frameId);
705
+ reject(new Error("thread.artifacts.list timed out"));
706
+ }, ARTIFACTS_LIST_TIMEOUT);
707
+ this.pendingArtifactsRequests.set(frameId, {
708
+ threadId,
709
+ resolve: (value) => {
710
+ clearTimeout(timer);
711
+ resolve(value);
712
+ },
713
+ reject: (err) => {
714
+ clearTimeout(timer);
715
+ reject(err);
716
+ },
717
+ timer
718
+ });
719
+ this.sendRaw({
720
+ id: frameId,
721
+ type: "thread.artifacts.list",
722
+ payload: {
723
+ thread_id: threadId,
724
+ cursor: options?.cursor ?? null,
725
+ limit: options?.limit,
726
+ direction: options?.direction ?? "all"
727
+ }
728
+ });
729
+ });
730
+ }
673
731
  // --- Settings ---
674
732
  subscribeSettings(options) {
675
733
  const audience = options?.audience ?? "user";
@@ -883,6 +941,8 @@ var AgentClient = class {
883
941
  case "ack": {
884
942
  if (frame.payload.ack_id === this.connectFrameId) {
885
943
  if (frame.payload.ok) {
944
+ const sv = frame.payload.server?.version?.trim();
945
+ this.callbacks.setServerVersion(sv || null);
886
946
  this.callbacks.setConnectionStatus("connected");
887
947
  this.callbacks.resetReconnect();
888
948
  this.requestThreadList();
@@ -915,6 +975,13 @@ var AgentClient = class {
915
975
  pending.reject(new Error(msg));
916
976
  this.callbacks.setSendError(msg);
917
977
  }
978
+ const artifactsPending = this.pendingArtifactsRequests.get(ackId);
979
+ if (artifactsPending) {
980
+ clearTimeout(artifactsPending.timer);
981
+ this.pendingArtifactsRequests.delete(ackId);
982
+ const msg = err.message || "Request rejected";
983
+ artifactsPending.reject(new Error(msg));
984
+ }
918
985
  }
919
986
  this.onError?.({
920
987
  code: err.code,
@@ -999,6 +1066,21 @@ var AgentClient = class {
999
1066
  );
1000
1067
  break;
1001
1068
  }
1069
+ case "thread.artifacts.list.result": {
1070
+ const threadId = frame.payload.thread_id;
1071
+ for (const [frameId, pending] of this.pendingArtifactsRequests) {
1072
+ if (pending.threadId !== threadId) continue;
1073
+ clearTimeout(pending.timer);
1074
+ this.pendingArtifactsRequests.delete(frameId);
1075
+ pending.resolve({
1076
+ artifacts: this.resolveArtifactUrls(frame.payload.artifacts.map(toThreadArtifact)),
1077
+ cursor: frame.payload.cursor,
1078
+ hasMore: frame.payload.has_more
1079
+ });
1080
+ break;
1081
+ }
1082
+ break;
1083
+ }
1002
1084
  case "thread.upsert":
1003
1085
  this.callbacks.onThreadUpsert(toThread(frame.payload.thread));
1004
1086
  break;
@@ -1060,6 +1142,7 @@ var AgentClient = class {
1060
1142
  this.stopHeartbeat();
1061
1143
  if (this.reconnectAttempts >= MAX_RECONNECT_ATTEMPTS) {
1062
1144
  this.abortPendingMessages("Connection lost after maximum retries");
1145
+ this.abortPendingArtifacts("Connection lost after maximum retries");
1063
1146
  this.callbacks.setReconnectFailed();
1064
1147
  this.callbacks.setConnectionStatus("error", "Connection lost after maximum retries");
1065
1148
  return;
@@ -1104,7 +1187,38 @@ var AgentClient = class {
1104
1187
  };
1105
1188
  });
1106
1189
  }
1190
+ resolveArtifactUrls(artifacts) {
1191
+ if (!this.httpBaseUrl) return artifacts;
1192
+ const base = this.httpBaseUrl;
1193
+ return artifacts.map((artifact) => {
1194
+ if (!artifact.url?.startsWith("/")) return artifact;
1195
+ return { ...artifact, url: `${base}${artifact.url}` };
1196
+ });
1197
+ }
1107
1198
  };
1199
+
1200
+ // src/helpers/server-version.ts
1201
+ var THREAD_ARTIFACTS_MIN_SERVER_VERSION = "0.17.1";
1202
+ function parseSemverParts(version) {
1203
+ const core = version.trim().split("-")[0]?.split("+")[0] ?? "";
1204
+ const [maj, min, pat] = core.split(".");
1205
+ return [Number(maj) || 0, Number(min) || 0, Number(pat) || 0];
1206
+ }
1207
+ function compareSemver(a, b) {
1208
+ const [aMaj, aMin, aPat] = parseSemverParts(a);
1209
+ const [bMaj, bMin, bPat] = parseSemverParts(b);
1210
+ if (aMaj !== bMaj) return aMaj < bMaj ? -1 : 1;
1211
+ if (aMin !== bMin) return aMin < bMin ? -1 : 1;
1212
+ if (aPat !== bPat) return aPat < bPat ? -1 : 1;
1213
+ return 0;
1214
+ }
1215
+ function isServerAtLeast(version, minimum) {
1216
+ if (!version?.trim()) return false;
1217
+ return compareSemver(version, minimum) >= 0;
1218
+ }
1219
+ function supportsThreadArtifactsList(serverVersion) {
1220
+ return isServerAtLeast(serverVersion, THREAD_ARTIFACTS_MIN_SERVER_VERSION);
1221
+ }
1108
1222
  function mergeDisplayContentParts(prev, next) {
1109
1223
  if (!next?.length) return prev;
1110
1224
  if (!prev?.length) return next;
@@ -1164,6 +1278,7 @@ function createAgentStore() {
1164
1278
  connection: {
1165
1279
  status: "idle",
1166
1280
  error: null,
1281
+ serverVersion: null,
1167
1282
  sendError: null,
1168
1283
  reconnectAttempts: 0,
1169
1284
  reconnectFailed: false
@@ -1197,9 +1312,13 @@ function createAgentStore() {
1197
1312
  ...s.connection,
1198
1313
  status,
1199
1314
  error: error ?? null,
1200
- ...status === "connected" ? { sendError: null } : {}
1315
+ ...status === "connected" ? { sendError: null } : {},
1316
+ ...status === "connecting" ? { serverVersion: null } : {}
1201
1317
  }
1202
1318
  })),
1319
+ setServerVersion: (version) => set((s) => ({
1320
+ connection: { ...s.connection, serverVersion: version }
1321
+ })),
1203
1322
  setSendError: (message) => set((s) => ({
1204
1323
  connection: { ...s.connection, sendError: message }
1205
1324
  })),
@@ -1756,13 +1875,22 @@ function useConnection() {
1756
1875
  const { client } = useAgentContext();
1757
1876
  const status = useStore((s) => s.connection.status);
1758
1877
  const error = useStore((s) => s.connection.error);
1878
+ const serverVersion = useStore((s) => s.connection.serverVersion);
1759
1879
  const sendError = useStore((s) => s.connection.sendError);
1760
1880
  const reconnectFailed = useStore((s) => s.connection.reconnectFailed);
1761
1881
  const reconnect = react.useCallback(() => client.retry(), [client]);
1762
1882
  const dismissSendError = react.useCallback(() => {
1763
1883
  client.clearSendError();
1764
1884
  }, [client]);
1765
- return { status, error, sendError, dismissSendError, reconnectFailed, reconnect };
1885
+ return {
1886
+ status,
1887
+ error,
1888
+ serverVersion,
1889
+ sendError,
1890
+ dismissSendError,
1891
+ reconnectFailed,
1892
+ reconnect
1893
+ };
1766
1894
  }
1767
1895
  function useFeedback(targetId, targetType = "event") {
1768
1896
  const { client } = useAgentContext();
@@ -2025,6 +2153,60 @@ function useThreadEvents(threadId) {
2025
2153
  );
2026
2154
  return { hasOlderEvents, loadOlderEvents };
2027
2155
  }
2156
+ function useThreadArtifacts(threadId, options) {
2157
+ const { client } = useAgentContext();
2158
+ const direction = options?.direction ?? "all";
2159
+ const enabled = options?.enabled !== false;
2160
+ const limit = options?.limit;
2161
+ const [artifacts, setArtifacts] = react.useState([]);
2162
+ const [cursor, setCursor] = react.useState(null);
2163
+ const [hasMore, setHasMore] = react.useState(false);
2164
+ const [isLoading, setIsLoading] = react.useState(false);
2165
+ const [error, setError] = react.useState(null);
2166
+ const cursorRef = react.useRef(null);
2167
+ const load = react.useCallback(
2168
+ async (append) => {
2169
+ if (!threadId) return;
2170
+ setIsLoading(true);
2171
+ setError(null);
2172
+ try {
2173
+ const result = await client.requestThreadArtifactsList(threadId, {
2174
+ cursor: append ? cursorRef.current : null,
2175
+ limit,
2176
+ direction
2177
+ });
2178
+ cursorRef.current = result.cursor;
2179
+ setCursor(result.cursor);
2180
+ setHasMore(result.hasMore);
2181
+ setArtifacts((prev) => append ? [...prev, ...result.artifacts] : result.artifacts);
2182
+ } catch (err) {
2183
+ setError(err instanceof Error ? err.message : "Failed to load artifacts");
2184
+ } finally {
2185
+ setIsLoading(false);
2186
+ }
2187
+ },
2188
+ [threadId, client, limit, direction]
2189
+ );
2190
+ react.useEffect(() => {
2191
+ if (!threadId || !enabled) {
2192
+ setArtifacts([]);
2193
+ setCursor(null);
2194
+ cursorRef.current = null;
2195
+ setHasMore(false);
2196
+ setError(null);
2197
+ return;
2198
+ }
2199
+ void load(false);
2200
+ }, [threadId, enabled, direction, load]);
2201
+ const loadMore = react.useCallback(() => {
2202
+ if (!hasMore || isLoading) return;
2203
+ void load(true);
2204
+ }, [hasMore, isLoading, load]);
2205
+ const refresh = react.useCallback(() => {
2206
+ void load(false);
2207
+ }, [load]);
2208
+ return { artifacts, cursor, hasMore, isLoading, error, loadMore, refresh };
2209
+ }
2028
2210
  function useThreadsList() {
2029
2211
  const threadOrder = useStore((s) => s.threadOrder);
2030
2212
  const threadMap = useStore((s) => s.threads);
@@ -2071,8 +2253,12 @@ function useUpload() {
2071
2253
  exports.AgentProvider = AgentProvider;
2072
2254
  exports.AgentServerClient = AgentServerClient;
2073
2255
  exports.PACKAGE_VERSION = PACKAGE_VERSION;
2256
+ exports.THREAD_ARTIFACTS_MIN_SERVER_VERSION = THREAD_ARTIFACTS_MIN_SERVER_VERSION;
2074
2257
  exports.apiMessagesUrlToWebSocketUrl = apiMessagesUrlToWebSocketUrl;
2258
+ exports.compareSemver = compareSemver;
2259
+ exports.isServerAtLeast = isServerAtLeast;
2075
2260
  exports.isSettingsFieldGroup = isSettingsFieldGroup;
2261
+ exports.supportsThreadArtifactsList = supportsThreadArtifactsList;
2076
2262
  exports.toChartEventData = toChartEventData;
2077
2263
  exports.toFormElement = toFormElement;
2078
2264
  exports.toFormEventData = toFormEventData;
@@ -2087,6 +2273,7 @@ exports.useFormFileUpload = useFormFileUpload;
2087
2273
  exports.useMessage = useMessage;
2088
2274
  exports.useSettings = useSettings;
2089
2275
  exports.useSettingsFileUpload = useSettingsFileUpload;
2276
+ exports.useThreadArtifacts = useThreadArtifacts;
2090
2277
  exports.useThreadDetails = useThreadDetails;
2091
2278
  exports.useThreadEvents = useThreadEvents;
2092
2279
  exports.useThreadsActions = useThreadsActions;