@browserbasehq/orca 3.2.0-preview.2 → 3.2.0-preview.4

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.
Files changed (167) hide show
  1. package/dist/cjs/lib/v3/agent/AnthropicCUAClient.js +5 -5
  2. package/dist/cjs/lib/v3/agent/AnthropicCUAClient.js.map +1 -1
  3. package/dist/cjs/lib/v3/agent/GoogleCUAClient.js +5 -5
  4. package/dist/cjs/lib/v3/agent/GoogleCUAClient.js.map +1 -1
  5. package/dist/cjs/lib/v3/agent/OpenAICUAClient.js +5 -5
  6. package/dist/cjs/lib/v3/agent/OpenAICUAClient.js.map +1 -1
  7. package/dist/cjs/lib/v3/agent/tools/act.js +1 -10
  8. package/dist/cjs/lib/v3/agent/tools/act.js.map +1 -1
  9. package/dist/cjs/lib/v3/agent/tools/ariaTree.js +1 -12
  10. package/dist/cjs/lib/v3/agent/tools/ariaTree.js.map +1 -1
  11. package/dist/cjs/lib/v3/agent/tools/braveSearch.js.map +1 -1
  12. package/dist/cjs/lib/v3/agent/tools/browserbaseSearch.js.map +1 -1
  13. package/dist/cjs/lib/v3/agent/tools/click.js.map +1 -1
  14. package/dist/cjs/lib/v3/agent/tools/clickAndHold.js.map +1 -1
  15. package/dist/cjs/lib/v3/agent/tools/dragAndDrop.js.map +1 -1
  16. package/dist/cjs/lib/v3/agent/tools/extract.js +1 -10
  17. package/dist/cjs/lib/v3/agent/tools/extract.js.map +1 -1
  18. package/dist/cjs/lib/v3/agent/tools/fillFormVision.js.map +1 -1
  19. package/dist/cjs/lib/v3/agent/tools/fillform.js +1 -10
  20. package/dist/cjs/lib/v3/agent/tools/fillform.js.map +1 -1
  21. package/dist/cjs/lib/v3/agent/tools/index.d.ts +2 -2
  22. package/dist/cjs/lib/v3/agent/tools/index.js +53 -5
  23. package/dist/cjs/lib/v3/agent/tools/index.js.map +1 -1
  24. package/dist/cjs/lib/v3/agent/tools/keys.d.ts +1 -1
  25. package/dist/cjs/lib/v3/agent/tools/keys.js.map +1 -1
  26. package/dist/cjs/lib/v3/agent/tools/type.js.map +1 -1
  27. package/dist/cjs/lib/v3/api.js +9 -2
  28. package/dist/cjs/lib/v3/api.js.map +1 -1
  29. package/dist/cjs/lib/v3/flowlogger/EventEmitter.d.ts +7 -0
  30. package/dist/cjs/lib/v3/flowlogger/EventEmitter.js +30 -0
  31. package/dist/cjs/lib/v3/flowlogger/EventEmitter.js.map +1 -0
  32. package/dist/cjs/lib/v3/flowlogger/EventSink.d.ts +44 -0
  33. package/dist/cjs/lib/v3/flowlogger/EventSink.js +217 -0
  34. package/dist/cjs/lib/v3/flowlogger/EventSink.js.map +1 -0
  35. package/dist/cjs/lib/v3/flowlogger/EventStore.d.ts +26 -0
  36. package/dist/cjs/lib/v3/flowlogger/EventStore.js +135 -0
  37. package/dist/cjs/lib/v3/flowlogger/EventStore.js.map +1 -0
  38. package/dist/{esm/lib/v3/flowLogger.d.ts → cjs/lib/v3/flowlogger/FlowLogger.d.ts} +32 -31
  39. package/dist/cjs/lib/v3/flowlogger/FlowLogger.js +591 -0
  40. package/dist/cjs/lib/v3/flowlogger/FlowLogger.js.map +1 -0
  41. package/dist/cjs/lib/v3/flowlogger/prettify.d.ts +6 -0
  42. package/dist/cjs/lib/v3/flowlogger/prettify.js +395 -0
  43. package/dist/cjs/lib/v3/flowlogger/prettify.js.map +1 -0
  44. package/dist/cjs/lib/v3/handlers/handlerUtils/actHandlerUtils.js +26 -28
  45. package/dist/cjs/lib/v3/handlers/handlerUtils/actHandlerUtils.js.map +1 -1
  46. package/dist/cjs/lib/v3/handlers/v3AgentHandler.js +2 -2
  47. package/dist/cjs/lib/v3/handlers/v3AgentHandler.js.map +1 -1
  48. package/dist/cjs/lib/v3/handlers/v3CuaAgentHandler.js +3 -5
  49. package/dist/cjs/lib/v3/handlers/v3CuaAgentHandler.js.map +1 -1
  50. package/dist/cjs/lib/v3/llm/aisdk.js +9 -9
  51. package/dist/cjs/lib/v3/llm/aisdk.js.map +1 -1
  52. package/dist/cjs/lib/v3/types/public/options.d.ts +2 -0
  53. package/dist/cjs/lib/v3/types/public/options.js.map +1 -1
  54. package/dist/cjs/lib/v3/understudy/cdp.d.ts +1 -1
  55. package/dist/cjs/lib/v3/understudy/cdp.js +83 -43
  56. package/dist/cjs/lib/v3/understudy/cdp.js.map +1 -1
  57. package/dist/cjs/lib/v3/understudy/page.js +18 -23
  58. package/dist/cjs/lib/v3/understudy/page.js.map +1 -1
  59. package/dist/cjs/lib/v3/v3.d.ts +5 -5
  60. package/dist/cjs/lib/v3/v3.js +48 -46
  61. package/dist/cjs/lib/v3/v3.js.map +1 -1
  62. package/dist/cjs/tests/integration/flowLogger.spec.d.ts +1 -0
  63. package/dist/cjs/tests/integration/flowLogger.spec.js +714 -0
  64. package/dist/cjs/tests/integration/flowLogger.spec.js.map +1 -0
  65. package/dist/cjs/tests/integration/testUtils.d.ts +33 -0
  66. package/dist/cjs/tests/integration/testUtils.js +144 -0
  67. package/dist/cjs/tests/integration/testUtils.js.map +1 -1
  68. package/dist/cjs/tests/integration/timeouts.spec.js +112 -2
  69. package/dist/cjs/tests/integration/timeouts.spec.js.map +1 -1
  70. package/dist/cjs/tests/unit/flowlogger-capturing-cdp.test.d.ts +1 -0
  71. package/dist/cjs/tests/unit/flowlogger-capturing-cdp.test.js +95 -0
  72. package/dist/cjs/tests/unit/flowlogger-capturing-cdp.test.js.map +1 -0
  73. package/dist/cjs/tests/unit/flowlogger-capturing-llm.test.d.ts +1 -0
  74. package/dist/cjs/tests/unit/flowlogger-capturing-llm.test.js +43 -0
  75. package/dist/cjs/tests/unit/flowlogger-capturing-llm.test.js.map +1 -0
  76. package/dist/cjs/tests/unit/flowlogger-eventstore.test.d.ts +1 -0
  77. package/dist/cjs/tests/unit/flowlogger-eventstore.test.js +250 -0
  78. package/dist/cjs/tests/unit/flowlogger-eventstore.test.js.map +1 -0
  79. package/dist/esm/lib/v3/agent/AnthropicCUAClient.js +1 -1
  80. package/dist/esm/lib/v3/agent/AnthropicCUAClient.js.map +1 -1
  81. package/dist/esm/lib/v3/agent/GoogleCUAClient.js +1 -1
  82. package/dist/esm/lib/v3/agent/GoogleCUAClient.js.map +1 -1
  83. package/dist/esm/lib/v3/agent/OpenAICUAClient.js +1 -1
  84. package/dist/esm/lib/v3/agent/OpenAICUAClient.js.map +1 -1
  85. package/dist/esm/lib/v3/agent/tools/act.js +1 -10
  86. package/dist/esm/lib/v3/agent/tools/act.js.map +1 -1
  87. package/dist/esm/lib/v3/agent/tools/ariaTree.js +1 -12
  88. package/dist/esm/lib/v3/agent/tools/ariaTree.js.map +1 -1
  89. package/dist/esm/lib/v3/agent/tools/braveSearch.js.map +1 -1
  90. package/dist/esm/lib/v3/agent/tools/browserbaseSearch.js.map +1 -1
  91. package/dist/esm/lib/v3/agent/tools/click.js.map +1 -1
  92. package/dist/esm/lib/v3/agent/tools/clickAndHold.js.map +1 -1
  93. package/dist/esm/lib/v3/agent/tools/dragAndDrop.js.map +1 -1
  94. package/dist/esm/lib/v3/agent/tools/extract.js +1 -10
  95. package/dist/esm/lib/v3/agent/tools/extract.js.map +1 -1
  96. package/dist/esm/lib/v3/agent/tools/fillFormVision.js.map +1 -1
  97. package/dist/esm/lib/v3/agent/tools/fillform.js +1 -10
  98. package/dist/esm/lib/v3/agent/tools/fillform.js.map +1 -1
  99. package/dist/esm/lib/v3/agent/tools/index.d.ts +2 -2
  100. package/dist/esm/lib/v3/agent/tools/index.js +53 -5
  101. package/dist/esm/lib/v3/agent/tools/index.js.map +1 -1
  102. package/dist/esm/lib/v3/agent/tools/keys.d.ts +1 -1
  103. package/dist/esm/lib/v3/agent/tools/keys.js.map +1 -1
  104. package/dist/esm/lib/v3/agent/tools/type.js.map +1 -1
  105. package/dist/esm/lib/v3/api.js +9 -2
  106. package/dist/esm/lib/v3/api.js.map +1 -1
  107. package/dist/esm/lib/v3/flowlogger/EventEmitter.d.ts +7 -0
  108. package/dist/esm/lib/v3/flowlogger/EventEmitter.js +26 -0
  109. package/dist/esm/lib/v3/flowlogger/EventEmitter.js.map +1 -0
  110. package/dist/esm/lib/v3/flowlogger/EventSink.d.ts +44 -0
  111. package/dist/esm/lib/v3/flowlogger/EventSink.js +206 -0
  112. package/dist/esm/lib/v3/flowlogger/EventSink.js.map +1 -0
  113. package/dist/esm/lib/v3/flowlogger/EventStore.d.ts +26 -0
  114. package/dist/esm/lib/v3/flowlogger/EventStore.js +127 -0
  115. package/dist/esm/lib/v3/flowlogger/EventStore.js.map +1 -0
  116. package/dist/{cjs/lib/v3/flowLogger.d.ts → esm/lib/v3/flowlogger/FlowLogger.d.ts} +32 -31
  117. package/dist/esm/lib/v3/flowlogger/FlowLogger.js +583 -0
  118. package/dist/esm/lib/v3/flowlogger/FlowLogger.js.map +1 -0
  119. package/dist/esm/lib/v3/flowlogger/prettify.d.ts +6 -0
  120. package/dist/esm/lib/v3/flowlogger/prettify.js +389 -0
  121. package/dist/esm/lib/v3/flowlogger/prettify.js.map +1 -0
  122. package/dist/esm/lib/v3/handlers/handlerUtils/actHandlerUtils.js +25 -27
  123. package/dist/esm/lib/v3/handlers/handlerUtils/actHandlerUtils.js.map +1 -1
  124. package/dist/esm/lib/v3/handlers/v3AgentHandler.js +1 -1
  125. package/dist/esm/lib/v3/handlers/v3AgentHandler.js.map +1 -1
  126. package/dist/esm/lib/v3/handlers/v3CuaAgentHandler.js +2 -4
  127. package/dist/esm/lib/v3/handlers/v3CuaAgentHandler.js.map +1 -1
  128. package/dist/esm/lib/v3/llm/aisdk.js +1 -1
  129. package/dist/esm/lib/v3/llm/aisdk.js.map +1 -1
  130. package/dist/esm/lib/v3/types/public/options.d.ts +2 -0
  131. package/dist/esm/lib/v3/types/public/options.js.map +1 -1
  132. package/dist/esm/lib/v3/understudy/cdp.d.ts +1 -1
  133. package/dist/esm/lib/v3/understudy/cdp.js +78 -38
  134. package/dist/esm/lib/v3/understudy/cdp.js.map +1 -1
  135. package/dist/esm/lib/v3/understudy/page.js +13 -18
  136. package/dist/esm/lib/v3/understudy/page.js.map +1 -1
  137. package/dist/esm/lib/v3/v3.d.ts +5 -5
  138. package/dist/esm/lib/v3/v3.js +43 -41
  139. package/dist/esm/lib/v3/v3.js.map +1 -1
  140. package/dist/esm/tests/integration/flowLogger.spec.d.ts +1 -0
  141. package/dist/esm/tests/integration/flowLogger.spec.js +712 -0
  142. package/dist/esm/tests/integration/flowLogger.spec.js.map +1 -0
  143. package/dist/esm/tests/integration/testUtils.d.ts +33 -0
  144. package/dist/esm/tests/integration/testUtils.js +138 -0
  145. package/dist/esm/tests/integration/testUtils.js.map +1 -1
  146. package/dist/esm/tests/integration/timeouts.spec.js +112 -2
  147. package/dist/esm/tests/integration/timeouts.spec.js.map +1 -1
  148. package/dist/esm/tests/unit/flowlogger-capturing-cdp.test.d.ts +1 -0
  149. package/dist/esm/tests/unit/flowlogger-capturing-cdp.test.js +93 -0
  150. package/dist/esm/tests/unit/flowlogger-capturing-cdp.test.js.map +1 -0
  151. package/dist/esm/tests/unit/flowlogger-capturing-llm.test.d.ts +1 -0
  152. package/dist/esm/tests/unit/flowlogger-capturing-llm.test.js +41 -0
  153. package/dist/esm/tests/unit/flowlogger-capturing-llm.test.js.map +1 -0
  154. package/dist/esm/tests/unit/flowlogger-eventstore.test.d.ts +1 -0
  155. package/dist/esm/tests/unit/flowlogger-eventstore.test.js +248 -0
  156. package/dist/esm/tests/unit/flowlogger-eventstore.test.js.map +1 -0
  157. package/package.json +3 -1
  158. package/dist/cjs/lib/v3/eventStore.d.ts +0 -41
  159. package/dist/cjs/lib/v3/eventStore.js +0 -375
  160. package/dist/cjs/lib/v3/eventStore.js.map +0 -1
  161. package/dist/cjs/lib/v3/flowLogger.js +0 -470
  162. package/dist/cjs/lib/v3/flowLogger.js.map +0 -1
  163. package/dist/esm/lib/v3/eventStore.d.ts +0 -41
  164. package/dist/esm/lib/v3/eventStore.js +0 -363
  165. package/dist/esm/lib/v3/eventStore.js.map +0 -1
  166. package/dist/esm/lib/v3/flowLogger.js +0 -462
  167. package/dist/esm/lib/v3/flowLogger.js.map +0 -1
@@ -0,0 +1,591 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.FlowLogger = exports.FlowEvent = exports.FlowEventInputSchema = exports.FlowEventDataSchema = void 0;
4
+ exports.extractLlmPromptSummary = extractLlmPromptSummary;
5
+ exports.extractLlmCuaPromptSummary = extractLlmCuaPromptSummary;
6
+ exports.extractLlmCuaResponseSummary = extractLlmCuaResponseSummary;
7
+ const node_async_hooks_1 = require("node:async_hooks");
8
+ const uuid_1 = require("uuid");
9
+ const zod_1 = require("zod");
10
+ // =============================================================================
11
+ // Flow Event Model
12
+ // =============================================================================
13
+ exports.FlowEventDataSchema = zod_1.z.record(zod_1.z.string(), zod_1.z.unknown());
14
+ exports.FlowEventInputSchema = zod_1.z.object({
15
+ eventType: zod_1.z.string(),
16
+ eventId: zod_1.z.string().optional(),
17
+ eventParentIds: zod_1.z.array(zod_1.z.string()).optional(),
18
+ eventCreatedAt: zod_1.z.string().optional(),
19
+ sessionId: zod_1.z.string().optional(),
20
+ data: exports.FlowEventDataSchema.optional(),
21
+ });
22
+ class FlowEvent {
23
+ // "ModuleMethodSomethingEvent" -> hashToSmallInt("Modu) -> 5. eventId = "...5"
24
+ static deriveEventIdSuffix(eventType) {
25
+ const prefixMatch = eventType.match(/^[A-Z][a-z0-9]*/);
26
+ const prefix = prefixMatch?.[0] ?? eventType.slice(0, 4);
27
+ let hash = 0;
28
+ for (const ch of prefix.slice(0, 4)) {
29
+ hash = (hash * 31 + ch.charCodeAt(0)) % 10;
30
+ }
31
+ return String(hash); // e.g. "0" or "9"
32
+ }
33
+ // Builds a sortable UUID-like event id while preserving a stable, human-friendly suffix derived from the event family.
34
+ static createEventId(eventType) {
35
+ const rawEventId = (0, uuid_1.v7)();
36
+ return `${rawEventId.slice(0, -1)}${FlowEvent.deriveEventIdSuffix(eventType)}`;
37
+ }
38
+ // Base required fields for all events:
39
+ eventType;
40
+ eventId;
41
+ eventParentIds;
42
+ eventCreatedAt;
43
+ // `sessionId` usually matches `browserbaseSessionId` today, but FlowLogger treats it as a generic Stagehand session identifier because those may diverge in the future.
44
+ sessionId;
45
+ data; // event payload (e.g. params, action, result, error, etc.)
46
+ // Normalizes the event shape used everywhere in the flow logger pipeline. This is called at emission time right before an event is attached to the event bus and any sinks.
47
+ constructor(input) {
48
+ if (!input.sessionId) {
49
+ throw new Error("FlowEvent.sessionId is required.");
50
+ }
51
+ if (input.eventType.endsWith("Event")) {
52
+ this.eventType = input.eventType;
53
+ }
54
+ else {
55
+ this.eventType = `${input.eventType}Event`;
56
+ }
57
+ this.eventId = input.eventId ?? FlowEvent.createEventId(this.eventType);
58
+ this.eventParentIds = input.eventParentIds ?? [];
59
+ this.eventCreatedAt = input.eventCreatedAt ?? new Date().toISOString();
60
+ this.sessionId = input.sessionId;
61
+ this.data = input.data ?? {};
62
+ }
63
+ }
64
+ exports.FlowEvent = FlowEvent;
65
+ // AsyncLocalStorage is the authoritative source for the active flow parent stack inside a single async call-chain.
66
+ const loggerContext = new node_async_hooks_1.AsyncLocalStorage();
67
+ // Converts raw inline image/base64 payload lengths into a compact kb string for LLM prompt summaries.
68
+ function dataToKb(data) {
69
+ return ((data.length * 0.75) / 1024).toFixed(1);
70
+ }
71
+ const CDP_EVENT_NAMES = {
72
+ call: "CdpCallEvent",
73
+ response: "CdpResponseEvent",
74
+ responseError: "CdpResponseErrorEvent",
75
+ message: "CdpMessageEvent",
76
+ };
77
+ class FlowLogger {
78
+ // Copies the mutable parts of a context before it is re-entered in a later async callback. This prevents later parent-stack mutations from leaking backward into stored snapshots.
79
+ static cloneContext(ctx) {
80
+ return {
81
+ ...ctx,
82
+ parentEvents: ctx.parentEvents.map((event) => ({
83
+ ...event,
84
+ eventParentIds: [...event.eventParentIds],
85
+ })),
86
+ };
87
+ }
88
+ // Chooses the safest context to re-enter when callers already have a stored context
89
+ // and ALS may or may not already contain one for the same session.
90
+ // If the current ALS stack extends the stored stack, we keep the richer ALS view.
91
+ // If the stored stack is deeper, we preserve that instead.
92
+ // If they diverge, we prefer the current ALS view because it reflects the currently executing call-chain.
93
+ static resolveReentryContext(context) {
94
+ const currentContext = loggerContext.getStore() ?? null;
95
+ // If ALS is empty or belongs to another session, the caller's stored
96
+ // snapshot is the only safe context we can re-enter.
97
+ if (!currentContext || currentContext.sessionId !== context.sessionId) {
98
+ return FlowLogger.cloneContext(context);
99
+ }
100
+ const providedParentIds = context.parentEvents.map((event) => event.eventId);
101
+ const currentParentIds = currentContext.parentEvents.map((event) => event.eventId);
102
+ const currentExtendsProvided = providedParentIds.every((eventId, index) => currentParentIds[index] === eventId);
103
+ // ALS already has the provided chain as a prefix, so we keep the richer
104
+ // currently-executing stack instead of truncating it.
105
+ if (currentExtendsProvided) {
106
+ return FlowLogger.cloneContext(currentContext);
107
+ }
108
+ const providedExtendsCurrent = currentParentIds.every((eventId, index) => providedParentIds[index] === eventId);
109
+ // The stored snapshot is deeper than the current ALS stack, which usually
110
+ // means we are re-entering from a later async callback and need to restore
111
+ // the missing parent chain.
112
+ if (providedExtendsCurrent) {
113
+ return FlowLogger.cloneContext(context);
114
+ }
115
+ // If the two chains diverged, prefer the live ALS chain because it reflects
116
+ // the work currently executing on this async path.
117
+ return FlowLogger.cloneContext(currentContext);
118
+ }
119
+ // Materializes and emits a single flow event on the active ALS context.
120
+ // This is the lowest-level write path used by all higher-level logging helpers
121
+ // after they have decided which parent chain and session the event belongs to.
122
+ static emit(event) {
123
+ const ctx = FlowLogger.currentContext;
124
+ const emittedEvent = new FlowEvent({
125
+ ...event,
126
+ eventParentIds: event.eventParentIds ??
127
+ ctx.parentEvents.map((parent) => parent.eventId),
128
+ sessionId: ctx.sessionId,
129
+ });
130
+ ctx.eventBus.emit(emittedEvent.eventType, emittedEvent);
131
+ return emittedEvent;
132
+ }
133
+ // Wraps a unit of async work with started/completed/error events while maintaining
134
+ // the parent stack inside the active context.
135
+ static async runWithAutoStatusEventLogging(options, originalMethod) {
136
+ const ctx = FlowLogger.currentContext;
137
+ const { data, eventParentIds, eventType } = options;
138
+ let caughtError = null;
139
+ // if eventParentIds is explicitly [], this is a root event, clear the parent events in context
140
+ if (eventParentIds && eventParentIds.length === 0) {
141
+ ctx.parentEvents = [];
142
+ }
143
+ const startedEvent = FlowLogger.emit({
144
+ eventType,
145
+ data,
146
+ eventParentIds,
147
+ });
148
+ // Push after emitting so nested work sees this event as its direct parent
149
+ // for the rest of the wrapped method's lifetime.
150
+ ctx.parentEvents.push(startedEvent);
151
+ try {
152
+ return await originalMethod();
153
+ }
154
+ catch (error) {
155
+ caughtError = error;
156
+ // Error events attach directly under the started event even though the
157
+ // stack is still live, so the failure edge is explicit in the tree.
158
+ FlowLogger.emit({
159
+ eventType: `${eventType}ErrorEvent`,
160
+ eventParentIds: [...startedEvent.eventParentIds, startedEvent.eventId],
161
+ data: {
162
+ error: error instanceof Error ? error.message : String(error),
163
+ durationMs: Date.now() - new Date(startedEvent.eventCreatedAt).getTime(),
164
+ },
165
+ });
166
+ throw error;
167
+ }
168
+ finally {
169
+ // Pop only the frame owned by this wrapper. If nested code has already
170
+ // mutated the stack unexpectedly, we skip the completed event rather than
171
+ // emitting a misleading lifecycle edge.
172
+ const parentEvent = ctx.parentEvents.pop();
173
+ if (parentEvent?.eventId === startedEvent.eventId && !caughtError) {
174
+ FlowLogger.emit({
175
+ eventType: `${eventType}CompletedEvent`,
176
+ eventParentIds: [
177
+ ...startedEvent.eventParentIds,
178
+ startedEvent.eventId,
179
+ ],
180
+ data: {
181
+ durationMs: Date.now() - new Date(startedEvent.eventCreatedAt).getTime(),
182
+ },
183
+ });
184
+ }
185
+ }
186
+ }
187
+ // Emits a CDP event under a caller-supplied context. CDP transport code uses this
188
+ // instead of `runWithLogging()` because request/response/message events
189
+ // are separate lifecycle edges with explicit parent ids.
190
+ static logCdpEvent(context, eventType, { method, params, result, error, targetId }, eventParentIds) {
191
+ if (method.endsWith(".enable") || method === "enable") {
192
+ return null;
193
+ }
194
+ if (eventType === "message" && FlowLogger.NOISY_CDP_EVENTS.has(method)) {
195
+ return null;
196
+ }
197
+ return loggerContext.run(FlowLogger.cloneContext(context), () => FlowLogger.emit({
198
+ eventType: CDP_EVENT_NAMES[eventType],
199
+ eventParentIds,
200
+ data: {
201
+ method,
202
+ params,
203
+ result,
204
+ error,
205
+ targetId,
206
+ },
207
+ }));
208
+ }
209
+ // Emits an LLM request/response event only when a flow context is active.
210
+ // LLM logging is best-effort, so callers should not fail if it is invoked outside a tracked async chain.
211
+ static emitLlmEvent(event) {
212
+ const context = FlowLogger.resolveContext();
213
+ if (!context) {
214
+ return;
215
+ }
216
+ loggerContext.run(context, () => {
217
+ FlowLogger.emit(event);
218
+ });
219
+ }
220
+ // Builds the one-line prompt summary used in LLM request events for AI SDK middleware calls.
221
+ static buildMiddlewarePromptSummary(params) {
222
+ const toolCount = Array.isArray(params.tools) ? params.tools.length : 0;
223
+ const messages = (params.prompt ?? []);
224
+ const lastMsg = messages
225
+ .filter((message) => message.role !== "system")
226
+ .pop();
227
+ let rolePrefix = lastMsg?.role ?? "?";
228
+ let promptSummary = `(no text) +{${toolCount} tools}`;
229
+ if (!lastMsg) {
230
+ return `?: ${promptSummary}`;
231
+ }
232
+ if (typeof lastMsg.content === "string") {
233
+ promptSummary = `${lastMsg.content} +{${toolCount} tools}`;
234
+ }
235
+ else if (Array.isArray(lastMsg.content)) {
236
+ const toolResult = lastMsg.content.find((part) => part.type === "tool-result");
237
+ if (toolResult) {
238
+ rolePrefix = `tool result: ${toolResult.toolName}()`;
239
+ if (toolResult.output?.type === "json" && toolResult.output.value) {
240
+ promptSummary = `${JSON.stringify(toolResult.output.value)} +{${toolCount} tools}`;
241
+ }
242
+ else if (Array.isArray(toolResult.output?.value)) {
243
+ promptSummary = `${extractLlmMessageSummary({
244
+ content: toolResult.output.value,
245
+ }) ?? "(no text)"} +{${toolCount} tools}`;
246
+ }
247
+ }
248
+ else {
249
+ promptSummary = `${extractLlmMessageSummary({ content: lastMsg.content }) ?? "(no text)"} +{${toolCount} tools}`;
250
+ }
251
+ }
252
+ return `${rolePrefix}: ${promptSummary}`;
253
+ }
254
+ // Builds the one-line output summary used in LLM response events for AI SDK middleware calls.
255
+ static buildMiddlewareOutputSummary(result) {
256
+ let outputSummary = result.text || "";
257
+ if (!outputSummary && result.content) {
258
+ if (typeof result.content === "string") {
259
+ outputSummary = result.content;
260
+ }
261
+ else if (Array.isArray(result.content)) {
262
+ outputSummary = result.content
263
+ .map((contentPart) => {
264
+ if (contentPart.text) {
265
+ return contentPart.text;
266
+ }
267
+ if (contentPart.type === "tool-call") {
268
+ return `tool call: ${contentPart.toolName}()`;
269
+ }
270
+ return `[${contentPart.type}]`;
271
+ })
272
+ .join(" ");
273
+ }
274
+ }
275
+ if (!outputSummary && result.toolCalls?.length) {
276
+ return `[${result.toolCalls.length} tool calls]`;
277
+ }
278
+ return outputSummary || "[empty]";
279
+ }
280
+ // =============================================================================
281
+ // Flow Logger Public Lifecycle API
282
+ // =============================================================================
283
+ // Initialize a new logging context. Call this at the start of a session.
284
+ static init(sessionId, eventBus) {
285
+ const ctx = {
286
+ sessionId,
287
+ eventBus,
288
+ parentEvents: [],
289
+ };
290
+ loggerContext.enterWith(ctx);
291
+ return ctx;
292
+ }
293
+ // Clears the parent stack for a session when a V3 instance shuts down.
294
+ // This does not emit a final event; it just tears down in-memory context.
295
+ static async close(context) {
296
+ const ctx = context ?? loggerContext.getStore() ?? null;
297
+ if (!ctx)
298
+ return;
299
+ ctx.parentEvents = [];
300
+ }
301
+ // Returns the current ALS-backed flow context and throws when code
302
+ // executes outside a tracked flow. Use `resolveContext()` for best-effort lookups.
303
+ static get currentContext() {
304
+ const ctx = loggerContext.getStore() ?? null;
305
+ if (!ctx) {
306
+ throw new Error("FlowLogger context is missing.");
307
+ }
308
+ return ctx;
309
+ }
310
+ // Returns a cloned FlowLogger context for the current async call-chain when one exists,
311
+ // otherwise falls back to the provided instance-owned context.
312
+ // This is the non-throwing lookup for callers that can continue without ALS.
313
+ static resolveContext(fallbackContext) {
314
+ const currentContext = loggerContext.getStore() ?? null;
315
+ if (currentContext) {
316
+ return FlowLogger.cloneContext(currentContext);
317
+ }
318
+ return fallbackContext ? FlowLogger.cloneContext(fallbackContext) : null;
319
+ }
320
+ // Decorator-style wrapper used on class methods that should emit their own started/completed/error envelope.
321
+ // It resolves the flow context from either the decorator options or `this.flowLoggerContext`,
322
+ // then delegates the actual lifecycle handling to `runWithLogging()`.
323
+ static wrapWithLogging(options) {
324
+ return function (originalMethod) {
325
+ const wrappedMethod = async function (...args) {
326
+ let context = options.context;
327
+ if (!context) {
328
+ context = this?.flowLoggerContext;
329
+ }
330
+ return await FlowLogger.runWithLogging({
331
+ ...options,
332
+ context,
333
+ }, (...boundArgs) => originalMethod.apply(this, boundArgs), args);
334
+ };
335
+ return wrappedMethod;
336
+ };
337
+ }
338
+ static runWithLogging(options, originalMethod, params) {
339
+ const eventData = {
340
+ ...(options.data ?? {}),
341
+ params: [...params],
342
+ };
343
+ const execute = () => FlowLogger.runWithAutoStatusEventLogging({
344
+ ...options,
345
+ data: eventData,
346
+ }, () => originalMethod(...params));
347
+ // No explicit context and no active ALS means there is nothing to attach
348
+ // this work to, so we leave execution untouched instead of fabricating a
349
+ // root event.
350
+ if (!options.context && !(loggerContext.getStore() ?? null)) {
351
+ return originalMethod(...params);
352
+ }
353
+ if (options.context) {
354
+ // Re-enter the caller-owned context so wrapper events land under the same
355
+ // session tree even when this code executes outside the original ALS
356
+ // chain.
357
+ return loggerContext.run(FlowLogger.resolveReentryContext(options.context), execute);
358
+ }
359
+ return execute();
360
+ }
361
+ // Re-enters an existing FlowLogger context without emitting wrapper events.
362
+ // Use this when work already belongs to a known parent and needs AsyncLocalStorage set manually.
363
+ static withContext(context, fn) {
364
+ return loggerContext.run(FlowLogger.resolveReentryContext(context), fn);
365
+ }
366
+ // ===========================================================================
367
+ // CDP Events
368
+ // ===========================================================================
369
+ static NOISY_CDP_EVENTS = new Set([
370
+ "Target.targetInfoChanged",
371
+ "Runtime.executionContextCreated",
372
+ "Runtime.executionContextDestroyed",
373
+ "Runtime.executionContextsCleared",
374
+ "Page.lifecycleEvent",
375
+ "Network.dataReceived",
376
+ "Network.loadingFinished",
377
+ "Network.requestWillBeSentExtraInfo",
378
+ "Network.responseReceivedExtraInfo",
379
+ "Network.requestWillBeSent",
380
+ "Network.responseReceived",
381
+ ]);
382
+ // Logs the start of a CDP command. CDP transport calls this before sending a
383
+ // message over the websocket so the eventual response can attach to it.
384
+ static logCdpCallEvent(context, data) {
385
+ return FlowLogger.logCdpEvent(context, "call", data);
386
+ }
387
+ // Logs the terminal response for a previously emitted CDP call event.
388
+ static logCdpResponseEvent(context, parentEvent, data) {
389
+ FlowLogger.logCdpEvent(context, data.error ? "responseError" : "response", data, [...parentEvent.eventParentIds, parentEvent.eventId]);
390
+ }
391
+ // Logs an unsolicited CDP message under the most recent related call event.
392
+ static logCdpMessageEvent(context, parentEvent, data) {
393
+ FlowLogger.logCdpEvent(context, "message", data, [
394
+ ...parentEvent.eventParentIds,
395
+ parentEvent.eventId,
396
+ ]);
397
+ }
398
+ // ===========================================================================
399
+ // LLM Events
400
+ // ===========================================================================
401
+ // Emits a best-effort LLM request event when logging occurs inside an active flow context.
402
+ static logLlmRequest({ requestId, model, prompt, }) {
403
+ FlowLogger.emitLlmEvent({
404
+ eventType: "LlmRequestEvent",
405
+ data: {
406
+ requestId,
407
+ model,
408
+ prompt,
409
+ },
410
+ });
411
+ }
412
+ // Emits a best-effort LLM response event when logging occurs inside an active flow context.
413
+ static logLlmResponse({ requestId, model, output, inputTokens, outputTokens, }) {
414
+ FlowLogger.emitLlmEvent({
415
+ eventType: "LlmResponseEvent",
416
+ data: {
417
+ requestId,
418
+ model,
419
+ output,
420
+ inputTokens,
421
+ outputTokens,
422
+ },
423
+ });
424
+ }
425
+ // ===========================================================================
426
+ // LLM Logging Middleware
427
+ // ===========================================================================
428
+ // Creates AI SDK middleware that wraps a generate call with FlowLogger LLM request/response events
429
+ // while leaving model execution behavior unchanged.
430
+ static createLlmLoggingMiddleware(modelId) {
431
+ return {
432
+ wrapGenerate: async ({ doGenerate, params }) => {
433
+ const llmRequestId = (0, uuid_1.v7)();
434
+ FlowLogger.logLlmRequest({
435
+ requestId: llmRequestId,
436
+ model: modelId,
437
+ prompt: FlowLogger.buildMiddlewarePromptSummary(params),
438
+ });
439
+ const result = await doGenerate();
440
+ const res = result;
441
+ FlowLogger.logLlmResponse({
442
+ requestId: llmRequestId,
443
+ model: modelId,
444
+ output: FlowLogger.buildMiddlewareOutputSummary(res),
445
+ inputTokens: result.usage?.inputTokens,
446
+ outputTokens: result.usage?.outputTokens,
447
+ });
448
+ return result;
449
+ },
450
+ };
451
+ }
452
+ }
453
+ exports.FlowLogger = FlowLogger;
454
+ // Extracts text and image markers from an LLM content array.
455
+ // This is shared by the request-summary helpers below so different provider message
456
+ // shapes render consistently in the flow log.
457
+ function extractLlmMessageContent(content) {
458
+ const result = {
459
+ text: undefined,
460
+ extras: [],
461
+ };
462
+ for (const part of content) {
463
+ const p = part;
464
+ // Text
465
+ if (!result.text && p.text) {
466
+ result.text = p.type === "text" || !p.type ? p.text : undefined;
467
+ }
468
+ // Images - various formats
469
+ if (p.type === "image" || p.type === "image_url") {
470
+ const url = p.image_url?.url;
471
+ if (url?.startsWith("data:"))
472
+ result.extras.push(`${dataToKb(url)}kb image`);
473
+ else if (p.source?.data)
474
+ result.extras.push(`${dataToKb(p.source.data)}kb image`);
475
+ else
476
+ result.extras.push("image");
477
+ }
478
+ else if (p.source?.data) {
479
+ result.extras.push(`${dataToKb(p.source.data)}kb image`);
480
+ }
481
+ else if (p.inlineData?.data) {
482
+ result.extras.push(`${dataToKb(p.inlineData.data)}kb image`);
483
+ }
484
+ // Recurse into tool_result content
485
+ if (p.type === "tool_result" && Array.isArray(p.content)) {
486
+ const nested = extractLlmMessageContent(p.content);
487
+ if (!result.text && nested.text) {
488
+ result.text = nested.text;
489
+ }
490
+ result.extras.push(...nested.extras);
491
+ }
492
+ }
493
+ return result;
494
+ }
495
+ // Produces a single compact summary from a provider-specific message payload
496
+ // so request and tool-result logs stay readable.
497
+ function extractLlmMessageSummary(input, options) {
498
+ const result = {
499
+ text: undefined,
500
+ extras: [...(options?.extras ?? [])],
501
+ };
502
+ if (typeof input.content === "string") {
503
+ result.text = input.content;
504
+ }
505
+ else if (typeof input.text === "string") {
506
+ result.text = input.text;
507
+ }
508
+ else if (Array.isArray(input.parts)) {
509
+ const summary = extractLlmMessageContent(input.parts);
510
+ result.text = summary.text;
511
+ result.extras.push(...summary.extras);
512
+ }
513
+ else if (Array.isArray(input.content)) {
514
+ const summary = extractLlmMessageContent(input.content);
515
+ result.text = summary.text;
516
+ result.extras.push(...summary.extras);
517
+ }
518
+ if (options?.trimInstructionPrefix && result.text) {
519
+ result.text = result.text.replace(/^[Ii]nstruction: /, "");
520
+ }
521
+ const text = result.text;
522
+ if (!text && result.extras.length === 0)
523
+ return undefined;
524
+ let summary = text || "";
525
+ if (result.extras.length > 0) {
526
+ const extrasStr = result.extras.map((e) => `+{${e}}`).join(" ");
527
+ summary = summary ? `${summary} ${extrasStr}` : extrasStr;
528
+ }
529
+ return summary || undefined;
530
+ }
531
+ // Formats the last user-facing prompt into the one-line form used by standard LLM request logs,
532
+ // for example: `some text +{5.8kb image} +{schema}`.
533
+ function extractLlmPromptSummary(messages, options) {
534
+ try {
535
+ const lastUserMsg = messages.filter((m) => m.role === "user").pop();
536
+ if (!lastUserMsg)
537
+ return undefined;
538
+ return extractLlmMessageSummary(lastUserMsg, {
539
+ trimInstructionPrefix: true,
540
+ extras: [
541
+ ...(options?.hasSchema ? ["schema"] : []),
542
+ ...(options?.toolCount ? [`${options.toolCount} tools`] : []),
543
+ ],
544
+ });
545
+ }
546
+ catch {
547
+ return undefined;
548
+ }
549
+ }
550
+ // Extract a text summary from CUA-style messages. This accepts Anthropic, OpenAI, and Google-style payloads.
551
+ function extractLlmCuaPromptSummary(messages) {
552
+ try {
553
+ const lastMsg = messages
554
+ .filter((m) => {
555
+ const msg = m;
556
+ return msg.role === "user" || msg.type === "tool_result";
557
+ })
558
+ .pop();
559
+ if (!lastMsg)
560
+ return undefined;
561
+ return extractLlmMessageSummary(lastMsg);
562
+ }
563
+ catch {
564
+ return undefined;
565
+ }
566
+ }
567
+ // Formats the response side of a CUA exchange into a single short log line.
568
+ function extractLlmCuaResponseSummary(output) {
569
+ try {
570
+ const items = output
571
+ ?.candidates?.[0]?.content?.parts ??
572
+ (Array.isArray(output) ? output : []);
573
+ const summary = items
574
+ .map((item) => {
575
+ const i = item;
576
+ if (i.text)
577
+ return i.text;
578
+ if (i.functionCall?.name)
579
+ return i.functionCall.name;
580
+ if (i.type === "tool_use" && i.name)
581
+ return i.name;
582
+ return i.type ?? "[item]";
583
+ })
584
+ .join(" ");
585
+ return summary;
586
+ }
587
+ catch {
588
+ return "[error]";
589
+ }
590
+ }
591
+ //# sourceMappingURL=FlowLogger.js.map