doover-js 0.4.7 → 0.4.9

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.
@@ -28,5 +28,6 @@ export declare class MessagesApi {
28
28
  patchMessage(agentId: string, channelName: string, messageId: string, body: MessageBody, params?: MessageMutationParams): Promise<unknown>;
29
29
  deleteMessage(agentId: string, channelName: string, messageId: string): Promise<unknown>;
30
30
  getMessageAttachment(agentId: string, channelName: string, messageId: string, attachmentId: string): Promise<Blob>;
31
+ getInvocationLogs<TLog = unknown>(agentId: string, channelName: string, messageId: string): Promise<TLog[]>;
31
32
  createMultipartPayload(jsonPayload: Record<string, unknown>, attachments: Array<Blob | File>): FormData;
32
33
  }
@@ -33,6 +33,9 @@ class MessagesApi {
33
33
  getMessageAttachment(agentId, channelName, messageId, attachmentId) {
34
34
  return this.rest.get(`/agents/${agentId}/channels/${channelName}/messages/${messageId}/attachments/${attachmentId}`);
35
35
  }
36
+ getInvocationLogs(agentId, channelName, messageId) {
37
+ return this.rest.get(`/agents/${agentId}/channels/${channelName}/messages/${messageId}/logs`);
38
+ }
36
39
  createMultipartPayload(jsonPayload, attachments) {
37
40
  const formData = new FormData();
38
41
  formData.set("json_payload", JSON.stringify(jsonPayload));
@@ -17,6 +17,8 @@ export { useChannelMessages, channelMessagesQueryKey, } from "./useChannelMessag
17
17
  export type { UseChannelMessagesOptions, UseChannelMessagesResult, } from "./useChannelMessages";
18
18
  export { useChannelMessage, channelMessageQueryKey, } from "./useChannelMessage";
19
19
  export type { UseChannelMessageOptions, UseChannelMessageResult, } from "./useChannelMessage";
20
+ export { useInvocationLogs, invocationLogsQueryKey, } from "./useInvocationLogs";
21
+ export type { UseInvocationLogsOptions } from "./useInvocationLogs";
20
22
  export { useSendRpc } from "./useSendRpc";
21
23
  export type { RpcCommandId, RpcStatusEvent, SendRpcVariables, UseSendRpcOptions, UseSendRpcResult, } from "./useSendRpc";
22
24
  export { useMultiAgentAggregates, multiAgentAggregatesQueryKey, } from "./useMultiAgentAggregates";
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.resetSharedQueryClient = exports.getSharedQueryClient = exports.useTurnCredentials = exports.multiAgentChannelMessagesQueryKey = exports.useMultiAgentChannelMessages = exports.multiAgentAggregatesQueryKey = exports.useMultiAgentAggregates = exports.useSendRpc = exports.channelMessageQueryKey = exports.useChannelMessage = exports.channelMessagesQueryKey = exports.useChannelMessages = exports.useUpdateMessage = exports.useUpdateAggregate = exports.useSendMessage = exports.useAgentChannel = exports.channelAggregateQueryKey = exports.useChannelAggregate = exports.useChannelSubscription = exports.agentConnectionsQueryKey = exports.useAgentConnections = exports.useConnectionState = exports.useDooverClient = exports.DooverProvider = void 0;
3
+ exports.resetSharedQueryClient = exports.getSharedQueryClient = exports.useTurnCredentials = exports.multiAgentChannelMessagesQueryKey = exports.useMultiAgentChannelMessages = exports.multiAgentAggregatesQueryKey = exports.useMultiAgentAggregates = exports.useSendRpc = exports.invocationLogsQueryKey = exports.useInvocationLogs = exports.channelMessageQueryKey = exports.useChannelMessage = exports.channelMessagesQueryKey = exports.useChannelMessages = exports.useUpdateMessage = exports.useUpdateAggregate = exports.useSendMessage = exports.useAgentChannel = exports.channelAggregateQueryKey = exports.useChannelAggregate = exports.useChannelSubscription = exports.agentConnectionsQueryKey = exports.useAgentConnections = exports.useConnectionState = exports.useDooverClient = exports.DooverProvider = void 0;
4
4
  var context_1 = require("./context");
5
5
  Object.defineProperty(exports, "DooverProvider", { enumerable: true, get: function () { return context_1.DooverProvider; } });
6
6
  Object.defineProperty(exports, "useDooverClient", { enumerable: true, get: function () { return context_1.useDooverClient; } });
@@ -28,6 +28,9 @@ Object.defineProperty(exports, "channelMessagesQueryKey", { enumerable: true, ge
28
28
  var useChannelMessage_1 = require("./useChannelMessage");
29
29
  Object.defineProperty(exports, "useChannelMessage", { enumerable: true, get: function () { return useChannelMessage_1.useChannelMessage; } });
30
30
  Object.defineProperty(exports, "channelMessageQueryKey", { enumerable: true, get: function () { return useChannelMessage_1.channelMessageQueryKey; } });
31
+ var useInvocationLogs_1 = require("./useInvocationLogs");
32
+ Object.defineProperty(exports, "useInvocationLogs", { enumerable: true, get: function () { return useInvocationLogs_1.useInvocationLogs; } });
33
+ Object.defineProperty(exports, "invocationLogsQueryKey", { enumerable: true, get: function () { return useInvocationLogs_1.invocationLogsQueryKey; } });
31
34
  var useSendRpc_1 = require("./useSendRpc");
32
35
  Object.defineProperty(exports, "useSendRpc", { enumerable: true, get: function () { return useSendRpc_1.useSendRpc; } });
33
36
  var useMultiAgentAggregates_1 = require("./useMultiAgentAggregates");
@@ -1,7 +1,9 @@
1
1
  import { type InfiniteData, type UseInfiniteQueryResult } from "@tanstack/react-query";
2
2
  import type { MessageStructure } from "../types/common";
3
3
  import type { ChannelIdentifier } from "../types/viewer";
4
- export declare function channelMessagesQueryKey(agentId: string | undefined, channelName: string | undefined): readonly ["doover", "agent", string | undefined, "channel", string | undefined, "messages"];
4
+ export declare function channelMessagesQueryKey(agentId: string | undefined, channelName: string | undefined, fields?: readonly string[]): readonly ["doover", "agent", string | undefined, "channel", string | undefined, "messages"] | readonly ["doover", "agent", string | undefined, "channel", string | undefined, "messages", {
5
+ readonly fields: string[];
6
+ }];
5
7
  export interface UseChannelMessagesOptions {
6
8
  limit?: number;
7
9
  /** If false, skip subscribing for live message-create updates. Defaults true. */
@@ -6,8 +6,17 @@ const react_1 = require("react");
6
6
  const react_query_1 = require("@tanstack/react-query");
7
7
  const context_1 = require("./context");
8
8
  const useChannelSubscription_1 = require("./useChannelSubscription");
9
- function channelMessagesQueryKey(agentId, channelName) {
10
- return ["doover", "agent", agentId, "channel", channelName, "messages"];
9
+ function channelMessagesQueryKey(agentId, channelName, fields) {
10
+ const key = ["doover", "agent", agentId, "channel", channelName, "messages"];
11
+ if (!fields || fields.length === 0)
12
+ return key;
13
+ // A `field_name`-filtered request returns a different message stream than
14
+ // the unfiltered (or differently-filtered) one, so it must live under its
15
+ // own cache entry — otherwise one filter's pagination cursor leaks into
16
+ // another's `getNextPageParam`, and the "next page" fetch ends up anchored
17
+ // to an unrelated (often much older) message id. Sort so call-site argument
18
+ // order doesn't fragment the cache.
19
+ return [...key, { fields: [...fields].sort() }];
11
20
  }
12
21
  /**
13
22
  * Paginated infinite query over `DooverDataProvider.getMessages`, with live
@@ -24,8 +33,19 @@ function useChannelMessages(identifier, options) {
24
33
  const initialBefore = options?.initialBefore;
25
34
  const after = options?.after;
26
35
  const autoPaginate = options?.autoPaginate ?? false;
27
- const key = channelMessagesQueryKey(agentId, channelName);
36
+ const key = channelMessagesQueryKey(agentId, channelName, fields);
28
37
  const onMessage = (0, react_1.useCallback)((message) => {
38
+ // This cache entry is scoped to `fields`; the gateway delivers every
39
+ // message on the channel, so drop live pushes whose payload doesn't
40
+ // carry one of those fields rather than letting them pollute it.
41
+ if (fields && fields.length > 0) {
42
+ const data = message.data;
43
+ if (!data ||
44
+ typeof data !== "object" ||
45
+ !fields.some((f) => f in data)) {
46
+ return;
47
+ }
48
+ }
29
49
  queryClient.setQueryData(key, (current) => {
30
50
  if (!current)
31
51
  return current;
@@ -39,7 +59,7 @@ function useChannelMessages(identifier, options) {
39
59
  });
40
60
  },
41
61
  // eslint-disable-next-line react-hooks/exhaustive-deps
42
- [queryClient, agentId, channelName]);
62
+ [queryClient, agentId, channelName, fields?.join(",")]);
43
63
  (0, useChannelSubscription_1.useChannelSubscription)(liveUpdates ? identifier : undefined, { onMessage });
44
64
  const query = (0, react_query_1.useInfiniteQuery)({
45
65
  queryKey: key,
@@ -0,0 +1,23 @@
1
+ import { type UseQueryResult } from "@tanstack/react-query";
2
+ import type { ChannelIdentifier } from "../types/viewer";
3
+ export declare function invocationLogsQueryKey(agentId: string | undefined, channelName: string | undefined, messageId: string | undefined): readonly ["doover", "agent", string | undefined, "channel", string | undefined, "message", string | undefined, "logs"];
4
+ export interface UseInvocationLogsOptions {
5
+ /** Defaults to true. Set false to skip the query (e.g. dialog closed). */
6
+ enabled?: boolean;
7
+ /**
8
+ * react-query retry behaviour. Defaults to false — the logs endpoint
9
+ * commonly 404s while logs are still materialising server-side, and a
10
+ * manual refetch button is friendlier than silent retry.
11
+ */
12
+ retry?: boolean | number;
13
+ }
14
+ /**
15
+ * Fetch the log entries for a single invocation message. The `/logs`
16
+ * endpoint is only meaningful for invocation messages (channel name like
17
+ * `dv-proc-inv-…`); other message types return 404.
18
+ *
19
+ * Generic on `TLog` — the shape of a single log entry. Defaults to `unknown`
20
+ * because log payloads are domain-specific (lambda telemetry, app-level
21
+ * structured logs, etc.) and doover-js does not own the canonical shape.
22
+ */
23
+ export declare function useInvocationLogs<TLog = unknown>(identifier: ChannelIdentifier, messageId: string | undefined, options?: UseInvocationLogsOptions): UseQueryResult<TLog[]>;
@@ -0,0 +1,41 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.invocationLogsQueryKey = invocationLogsQueryKey;
4
+ exports.useInvocationLogs = useInvocationLogs;
5
+ const react_query_1 = require("@tanstack/react-query");
6
+ const context_1 = require("./context");
7
+ function invocationLogsQueryKey(agentId, channelName, messageId) {
8
+ return [
9
+ "doover",
10
+ "agent",
11
+ agentId,
12
+ "channel",
13
+ channelName,
14
+ "message",
15
+ messageId,
16
+ "logs",
17
+ ];
18
+ }
19
+ /**
20
+ * Fetch the log entries for a single invocation message. The `/logs`
21
+ * endpoint is only meaningful for invocation messages (channel name like
22
+ * `dv-proc-inv-…`); other message types return 404.
23
+ *
24
+ * Generic on `TLog` — the shape of a single log entry. Defaults to `unknown`
25
+ * because log payloads are domain-specific (lambda telemetry, app-level
26
+ * structured logs, etc.) and doover-js does not own the canonical shape.
27
+ */
28
+ function useInvocationLogs(identifier, messageId, options) {
29
+ const client = (0, context_1.useDooverClient)();
30
+ const { agentId, channelName } = identifier;
31
+ return (0, react_query_1.useQuery)({
32
+ queryKey: invocationLogsQueryKey(agentId, channelName, messageId),
33
+ queryFn: () => client.messages.getInvocationLogs(agentId, channelName, messageId),
34
+ enabled: !!agentId &&
35
+ !!channelName &&
36
+ !!messageId &&
37
+ (options?.enabled ?? true),
38
+ retry: options?.retry ?? false,
39
+ staleTime: Infinity,
40
+ });
41
+ }
@@ -336,6 +336,42 @@ function wrapper(client) {
336
336
  });
337
337
  await (0, react_1.waitFor)(() => (0, chai_1.expect)(result.current.data?.response?.status).to.equal("success"));
338
338
  });
339
+ (0, mocha_1.it)("useInvocationLogs fetches logs for the message", async () => {
340
+ const messageId = (0, snowflake_1.generateSnowflakeIdAtTime)(new Date("2026-01-01T00:00:00.000Z"));
341
+ const fetchMock = (0, helpers_1.createFetchMock)(() => (0, helpers_1.createJsonResponse)([
342
+ { level: "INFO", message: "hello", timestamp: 1 },
343
+ { level: "ERROR", message: "boom", timestamp: 2 },
344
+ ]));
345
+ const client = new doover_client_1.DooverClient({
346
+ dataRestUrl: "https://api.example.com",
347
+ controlApiUrl: "https://control.example.com",
348
+ dataWssUrl: "wss://ws.example.com",
349
+ fetchImpl: fetchMock,
350
+ webSocketImpl: helpers_1.MockWebSocket,
351
+ disableBrowserLifecycleHooks: true,
352
+ });
353
+ const { result } = (0, react_1.renderHook)(() => (0, react_2.useInvocationLogs)({ agentId: "a1", channelName: "dv-proc-inv-1" }, messageId), { wrapper: wrapper(client) });
354
+ await (0, react_1.waitFor)(() => (0, chai_1.expect)(result.current.isSuccess).to.equal(true));
355
+ (0, chai_1.expect)(result.current.data?.map((l) => l.level)).to.deep.equal([
356
+ "INFO",
357
+ "ERROR",
358
+ ]);
359
+ const requestUrl = fetchMock.firstCall?.args[0];
360
+ (0, chai_1.expect)(requestUrl).to.contain(`/agents/a1/channels/dv-proc-inv-1/messages/${messageId}/logs`);
361
+ });
362
+ (0, mocha_1.it)("useInvocationLogs is disabled when messageId is undefined", () => {
363
+ const fetchMock = (0, helpers_1.createFetchMock)(() => (0, helpers_1.createJsonResponse)([]));
364
+ const client = new doover_client_1.DooverClient({
365
+ dataRestUrl: "https://api.example.com",
366
+ controlApiUrl: "https://control.example.com",
367
+ dataWssUrl: "wss://ws.example.com",
368
+ fetchImpl: fetchMock,
369
+ webSocketImpl: helpers_1.MockWebSocket,
370
+ disableBrowserLifecycleHooks: true,
371
+ });
372
+ (0, react_1.renderHook)(() => (0, react_2.useInvocationLogs)({ agentId: "a1", channelName: "dv-proc-inv-1" }, undefined), { wrapper: wrapper(client) });
373
+ (0, chai_1.expect)(fetchMock.callCount).to.equal(0);
374
+ });
339
375
  (0, mocha_1.it)("getSharedQueryClient returns the same QueryClient across callers", () => {
340
376
  (0, react_2.resetSharedQueryClient)();
341
377
  const a = (0, react_2.getSharedQueryClient)();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "doover-js",
3
- "version": "0.4.7",
3
+ "version": "0.4.9",
4
4
  "description": "TypeScript client for Doover.",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",