lsd-pi 1.3.2 → 1.3.7

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 (205) hide show
  1. package/dist/cli.js +2 -1
  2. package/dist/lsd-settings-manager.d.ts +2 -0
  3. package/dist/lsd-settings-manager.js +5 -0
  4. package/dist/resource-loader.js +33 -3
  5. package/dist/resources/extensions/browser-tools/tools/codegen.js +5 -5
  6. package/dist/resources/extensions/browser-tools/tools/navigation.js +107 -178
  7. package/dist/resources/extensions/browser-tools/tools/network-mock.js +112 -167
  8. package/dist/resources/extensions/browser-tools/tools/pages.js +182 -234
  9. package/dist/resources/extensions/browser-tools/tools/refs.js +202 -461
  10. package/dist/resources/extensions/browser-tools/tools/session.js +176 -323
  11. package/dist/resources/extensions/browser-tools/tools/state-persistence.js +91 -154
  12. package/dist/resources/extensions/browser-tools/utils.js +1 -1
  13. package/dist/resources/extensions/cache-timer/index.js +3 -2
  14. package/dist/resources/extensions/slash-commands/extension-manifest.json +2 -2
  15. package/dist/resources/extensions/slash-commands/fast.js +73 -0
  16. package/dist/resources/extensions/slash-commands/index.js +2 -0
  17. package/dist/resources/extensions/slash-commands/plan.js +37 -12
  18. package/dist/resources/extensions/subagent/background-job-manager.js +13 -0
  19. package/dist/resources/extensions/subagent/in-process-runner.js +387 -0
  20. package/dist/resources/extensions/subagent/index.js +278 -626
  21. package/dist/resources/extensions/subagent/legacy-runner.js +503 -0
  22. package/dist/resources/extensions/voice/index.js +96 -36
  23. package/dist/resources/extensions/voice/push-to-talk.js +26 -0
  24. package/dist/welcome-screen.js +2 -2
  25. package/package.json +1 -1
  26. package/packages/pi-agent-core/dist/agent.d.ts +19 -0
  27. package/packages/pi-agent-core/dist/agent.d.ts.map +1 -1
  28. package/packages/pi-agent-core/dist/agent.js +16 -0
  29. package/packages/pi-agent-core/dist/agent.js.map +1 -1
  30. package/packages/pi-agent-core/src/agent.ts +32 -2
  31. package/packages/pi-ai/dist/providers/openai-codex-responses.d.ts +34 -1
  32. package/packages/pi-ai/dist/providers/openai-codex-responses.d.ts.map +1 -1
  33. package/packages/pi-ai/dist/providers/openai-codex-responses.js +32 -4
  34. package/packages/pi-ai/dist/providers/openai-codex-responses.js.map +1 -1
  35. package/packages/pi-ai/dist/providers/openai-codex-responses.test.js +127 -16
  36. package/packages/pi-ai/dist/providers/openai-codex-responses.test.js.map +1 -1
  37. package/packages/pi-ai/dist/providers/openai-responses.d.ts +8 -1
  38. package/packages/pi-ai/dist/providers/openai-responses.d.ts.map +1 -1
  39. package/packages/pi-ai/dist/providers/openai-responses.fast-mode.test.d.ts +2 -0
  40. package/packages/pi-ai/dist/providers/openai-responses.fast-mode.test.d.ts.map +1 -0
  41. package/packages/pi-ai/dist/providers/openai-responses.fast-mode.test.js +67 -0
  42. package/packages/pi-ai/dist/providers/openai-responses.fast-mode.test.js.map +1 -0
  43. package/packages/pi-ai/dist/providers/openai-responses.js +21 -3
  44. package/packages/pi-ai/dist/providers/openai-responses.js.map +1 -1
  45. package/packages/pi-ai/dist/providers/simple-options.d.ts.map +1 -1
  46. package/packages/pi-ai/dist/providers/simple-options.js +2 -0
  47. package/packages/pi-ai/dist/providers/simple-options.js.map +1 -1
  48. package/packages/pi-ai/dist/types.d.ts +5 -0
  49. package/packages/pi-ai/dist/types.d.ts.map +1 -1
  50. package/packages/pi-ai/dist/types.js.map +1 -1
  51. package/packages/pi-ai/src/providers/openai-codex-responses.test.ts +143 -20
  52. package/packages/pi-ai/src/providers/openai-codex-responses.ts +47 -4
  53. package/packages/pi-ai/src/providers/openai-responses.fast-mode.test.ts +73 -0
  54. package/packages/pi-ai/src/providers/openai-responses.ts +26 -3
  55. package/packages/pi-ai/src/providers/simple-options.ts +2 -0
  56. package/packages/pi-ai/src/types.ts +5 -0
  57. package/packages/pi-coding-agent/dist/core/keybindings.d.ts +1 -1
  58. package/packages/pi-coding-agent/dist/core/keybindings.d.ts.map +1 -1
  59. package/packages/pi-coding-agent/dist/core/keybindings.js +2 -0
  60. package/packages/pi-coding-agent/dist/core/keybindings.js.map +1 -1
  61. package/packages/pi-coding-agent/dist/core/sdk.d.ts.map +1 -1
  62. package/packages/pi-coding-agent/dist/core/sdk.js +4 -2
  63. package/packages/pi-coding-agent/dist/core/sdk.js.map +1 -1
  64. package/packages/pi-coding-agent/dist/core/settings-manager.collapse-tool-calls.test.d.ts +2 -0
  65. package/packages/pi-coding-agent/dist/core/settings-manager.collapse-tool-calls.test.d.ts.map +1 -0
  66. package/packages/pi-coding-agent/dist/core/settings-manager.collapse-tool-calls.test.js +35 -0
  67. package/packages/pi-coding-agent/dist/core/settings-manager.collapse-tool-calls.test.js.map +1 -0
  68. package/packages/pi-coding-agent/dist/core/settings-manager.d.ts +12 -0
  69. package/packages/pi-coding-agent/dist/core/settings-manager.d.ts.map +1 -1
  70. package/packages/pi-coding-agent/dist/core/settings-manager.fast-mode.test.d.ts +2 -0
  71. package/packages/pi-coding-agent/dist/core/settings-manager.fast-mode.test.d.ts.map +1 -0
  72. package/packages/pi-coding-agent/dist/core/settings-manager.fast-mode.test.js +35 -0
  73. package/packages/pi-coding-agent/dist/core/settings-manager.fast-mode.test.js.map +1 -0
  74. package/packages/pi-coding-agent/dist/core/settings-manager.js +24 -0
  75. package/packages/pi-coding-agent/dist/core/settings-manager.js.map +1 -1
  76. package/packages/pi-coding-agent/dist/core/slash-commands.d.ts.map +1 -1
  77. package/packages/pi-coding-agent/dist/core/slash-commands.js +1 -0
  78. package/packages/pi-coding-agent/dist/core/slash-commands.js.map +1 -1
  79. package/packages/pi-coding-agent/dist/core/system-prompt.d.ts.map +1 -1
  80. package/packages/pi-coding-agent/dist/core/system-prompt.js +6 -1
  81. package/packages/pi-coding-agent/dist/core/system-prompt.js.map +1 -1
  82. package/packages/pi-coding-agent/dist/core/tool-priority.d.ts +4 -0
  83. package/packages/pi-coding-agent/dist/core/tool-priority.d.ts.map +1 -0
  84. package/packages/pi-coding-agent/dist/core/tool-priority.js +18 -0
  85. package/packages/pi-coding-agent/dist/core/tool-priority.js.map +1 -0
  86. package/packages/pi-coding-agent/dist/core/tool-priority.test.d.ts +2 -0
  87. package/packages/pi-coding-agent/dist/core/tool-priority.test.d.ts.map +1 -0
  88. package/packages/pi-coding-agent/dist/core/tool-priority.test.js +27 -0
  89. package/packages/pi-coding-agent/dist/core/tool-priority.test.js.map +1 -0
  90. package/packages/pi-coding-agent/dist/core/tools/edit-diff.d.ts +5 -0
  91. package/packages/pi-coding-agent/dist/core/tools/edit-diff.d.ts.map +1 -1
  92. package/packages/pi-coding-agent/dist/core/tools/edit-diff.js +21 -0
  93. package/packages/pi-coding-agent/dist/core/tools/edit-diff.js.map +1 -1
  94. package/packages/pi-coding-agent/dist/core/tools/edit-diff.test.js +16 -1
  95. package/packages/pi-coding-agent/dist/core/tools/edit-diff.test.js.map +1 -1
  96. package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/tool-summary-line.test.d.ts +2 -0
  97. package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/tool-summary-line.test.d.ts.map +1 -0
  98. package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/tool-summary-line.test.js +34 -0
  99. package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/tool-summary-line.test.js.map +1 -0
  100. package/packages/pi-coding-agent/dist/modes/interactive/components/btw-overlay.d.ts +45 -0
  101. package/packages/pi-coding-agent/dist/modes/interactive/components/btw-overlay.d.ts.map +1 -0
  102. package/packages/pi-coding-agent/dist/modes/interactive/components/btw-overlay.js +314 -0
  103. package/packages/pi-coding-agent/dist/modes/interactive/components/btw-overlay.js.map +1 -0
  104. package/packages/pi-coding-agent/dist/modes/interactive/components/btw-overlay.test.d.ts +2 -0
  105. package/packages/pi-coding-agent/dist/modes/interactive/components/btw-overlay.test.d.ts.map +1 -0
  106. package/packages/pi-coding-agent/dist/modes/interactive/components/btw-overlay.test.js +122 -0
  107. package/packages/pi-coding-agent/dist/modes/interactive/components/btw-overlay.test.js.map +1 -0
  108. package/packages/pi-coding-agent/dist/modes/interactive/components/diff.d.ts +7 -5
  109. package/packages/pi-coding-agent/dist/modes/interactive/components/diff.d.ts.map +1 -1
  110. package/packages/pi-coding-agent/dist/modes/interactive/components/diff.js +86 -28
  111. package/packages/pi-coding-agent/dist/modes/interactive/components/diff.js.map +1 -1
  112. package/packages/pi-coding-agent/dist/modes/interactive/components/footer.d.ts +4 -0
  113. package/packages/pi-coding-agent/dist/modes/interactive/components/footer.d.ts.map +1 -1
  114. package/packages/pi-coding-agent/dist/modes/interactive/components/footer.js +23 -10
  115. package/packages/pi-coding-agent/dist/modes/interactive/components/footer.js.map +1 -1
  116. package/packages/pi-coding-agent/dist/modes/interactive/components/settings-selector.d.ts +8 -0
  117. package/packages/pi-coding-agent/dist/modes/interactive/components/settings-selector.d.ts.map +1 -1
  118. package/packages/pi-coding-agent/dist/modes/interactive/components/settings-selector.js +52 -6
  119. package/packages/pi-coding-agent/dist/modes/interactive/components/settings-selector.js.map +1 -1
  120. package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.d.ts +19 -0
  121. package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.d.ts.map +1 -1
  122. package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.js +127 -14
  123. package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.js.map +1 -1
  124. package/packages/pi-coding-agent/dist/modes/interactive/components/tool-summary-line.d.ts +14 -0
  125. package/packages/pi-coding-agent/dist/modes/interactive/components/tool-summary-line.d.ts.map +1 -0
  126. package/packages/pi-coding-agent/dist/modes/interactive/components/tool-summary-line.js +93 -0
  127. package/packages/pi-coding-agent/dist/modes/interactive/components/tool-summary-line.js.map +1 -0
  128. package/packages/pi-coding-agent/dist/modes/interactive/controllers/__tests__/chat-controller.collapsed-tool-summary.test.d.ts +2 -0
  129. package/packages/pi-coding-agent/dist/modes/interactive/controllers/__tests__/chat-controller.collapsed-tool-summary.test.d.ts.map +1 -0
  130. package/packages/pi-coding-agent/dist/modes/interactive/controllers/__tests__/chat-controller.collapsed-tool-summary.test.js +328 -0
  131. package/packages/pi-coding-agent/dist/modes/interactive/controllers/__tests__/chat-controller.collapsed-tool-summary.test.js.map +1 -0
  132. package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.d.ts.map +1 -1
  133. package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.js +123 -0
  134. package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.js.map +1 -1
  135. package/packages/pi-coding-agent/dist/modes/interactive/controllers/extension-ui-controller.js +1 -1
  136. package/packages/pi-coding-agent/dist/modes/interactive/controllers/extension-ui-controller.js.map +1 -1
  137. package/packages/pi-coding-agent/dist/modes/interactive/controllers/input-controller.d.ts.map +1 -1
  138. package/packages/pi-coding-agent/dist/modes/interactive/controllers/input-controller.js +7 -0
  139. package/packages/pi-coding-agent/dist/modes/interactive/controllers/input-controller.js.map +1 -1
  140. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode-state.d.ts +4 -0
  141. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode-state.d.ts.map +1 -1
  142. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode-state.js.map +1 -1
  143. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.d.ts +9 -1
  144. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  145. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.js +103 -23
  146. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.js.map +1 -1
  147. package/packages/pi-coding-agent/dist/modes/interactive/slash-command-handlers.d.ts.map +1 -1
  148. package/packages/pi-coding-agent/dist/modes/interactive/slash-command-handlers.js +41 -0
  149. package/packages/pi-coding-agent/dist/modes/interactive/slash-command-handlers.js.map +1 -1
  150. package/packages/pi-coding-agent/dist/modes/interactive/theme/themes.js +4 -4
  151. package/packages/pi-coding-agent/dist/modes/interactive/theme/themes.js.map +1 -1
  152. package/packages/pi-coding-agent/package.json +1 -1
  153. package/packages/pi-coding-agent/src/core/keybindings.ts +4 -1
  154. package/packages/pi-coding-agent/src/core/sdk.ts +4 -2
  155. package/packages/pi-coding-agent/src/core/settings-manager.collapse-tool-calls.test.ts +46 -0
  156. package/packages/pi-coding-agent/src/core/settings-manager.fast-mode.test.ts +46 -0
  157. package/packages/pi-coding-agent/src/core/settings-manager.ts +36 -0
  158. package/packages/pi-coding-agent/src/core/slash-commands.ts +1 -0
  159. package/packages/pi-coding-agent/src/core/system-prompt.ts +6 -1
  160. package/packages/pi-coding-agent/src/core/tool-priority.test.ts +30 -0
  161. package/packages/pi-coding-agent/src/core/tool-priority.ts +17 -0
  162. package/packages/pi-coding-agent/src/core/tools/edit-diff.test.ts +20 -0
  163. package/packages/pi-coding-agent/src/core/tools/edit-diff.ts +26 -0
  164. package/packages/pi-coding-agent/src/modes/interactive/components/__tests__/tool-summary-line.test.ts +41 -0
  165. package/packages/pi-coding-agent/src/modes/interactive/components/btw-overlay.test.ts +172 -0
  166. package/packages/pi-coding-agent/src/modes/interactive/components/btw-overlay.ts +402 -0
  167. package/packages/pi-coding-agent/src/modes/interactive/components/diff.ts +105 -28
  168. package/packages/pi-coding-agent/src/modes/interactive/components/footer.ts +21 -6
  169. package/packages/pi-coding-agent/src/modes/interactive/components/settings-selector.ts +63 -6
  170. package/packages/pi-coding-agent/src/modes/interactive/components/tool-execution.ts +1262 -1138
  171. package/packages/pi-coding-agent/src/modes/interactive/components/tool-summary-line.ts +120 -0
  172. package/packages/pi-coding-agent/src/modes/interactive/controllers/__tests__/chat-controller.collapsed-tool-summary.test.ts +396 -0
  173. package/packages/pi-coding-agent/src/modes/interactive/controllers/chat-controller.ts +530 -398
  174. package/packages/pi-coding-agent/src/modes/interactive/controllers/extension-ui-controller.ts +1 -1
  175. package/packages/pi-coding-agent/src/modes/interactive/controllers/input-controller.ts +7 -0
  176. package/packages/pi-coding-agent/src/modes/interactive/interactive-mode-state.ts +4 -0
  177. package/packages/pi-coding-agent/src/modes/interactive/interactive-mode.ts +109 -23
  178. package/packages/pi-coding-agent/src/modes/interactive/slash-command-handlers.ts +60 -1
  179. package/packages/pi-coding-agent/src/modes/interactive/theme/themes.ts +4 -4
  180. package/packages/pi-tui/dist/components/editor.js +3 -3
  181. package/packages/pi-tui/dist/components/editor.js.map +1 -1
  182. package/packages/pi-tui/src/components/editor.ts +3 -3
  183. package/pkg/dist/modes/interactive/theme/themes.js +4 -4
  184. package/pkg/dist/modes/interactive/theme/themes.js.map +1 -1
  185. package/pkg/package.json +1 -1
  186. package/src/resources/extensions/browser-tools/tools/codegen.ts +5 -5
  187. package/src/resources/extensions/browser-tools/tools/navigation.ts +118 -196
  188. package/src/resources/extensions/browser-tools/tools/network-mock.ts +114 -205
  189. package/src/resources/extensions/browser-tools/tools/pages.ts +183 -237
  190. package/src/resources/extensions/browser-tools/tools/refs.ts +193 -507
  191. package/src/resources/extensions/browser-tools/tools/session.ts +182 -321
  192. package/src/resources/extensions/browser-tools/tools/state-persistence.ts +94 -172
  193. package/src/resources/extensions/browser-tools/utils.ts +1 -1
  194. package/src/resources/extensions/cache-timer/index.ts +3 -2
  195. package/src/resources/extensions/slash-commands/extension-manifest.json +2 -2
  196. package/src/resources/extensions/slash-commands/fast.ts +89 -0
  197. package/src/resources/extensions/slash-commands/index.ts +2 -0
  198. package/src/resources/extensions/slash-commands/plan.ts +42 -12
  199. package/src/resources/extensions/subagent/background-job-manager.ts +28 -0
  200. package/src/resources/extensions/subagent/in-process-runner.ts +534 -0
  201. package/src/resources/extensions/subagent/index.ts +489 -799
  202. package/src/resources/extensions/subagent/legacy-runner.ts +607 -0
  203. package/src/resources/extensions/voice/index.ts +308 -238
  204. package/src/resources/extensions/voice/push-to-talk.ts +42 -0
  205. package/src/resources/extensions/voice/tests/push-to-talk.test.ts +109 -0
@@ -16,360 +16,213 @@ export function registerSessionTools(pi, deps) {
16
16
  async execute(_toolCallId, _params, _signal, _onUpdate, _ctx) {
17
17
  try {
18
18
  await deps.closeBrowser();
19
- return {
20
- content: [{ type: "text", text: "Browser closed." }],
21
- details: {},
22
- };
19
+ return { content: [{ type: "text", text: "Browser closed." }], details: {} };
23
20
  }
24
21
  catch (err) {
25
- return {
26
- content: [{ type: "text", text: `Close failed: ${err.message}` }],
27
- details: { error: err.message },
28
- isError: true,
29
- };
22
+ return { content: [{ type: "text", text: `Close failed: ${err.message}` }], details: { error: err.message }, isError: true };
30
23
  }
31
24
  },
32
25
  });
33
26
  // -------------------------------------------------------------------------
34
- // browser_trace_start
27
+ // browser_trace — start, stop, export HAR
35
28
  // -------------------------------------------------------------------------
36
29
  pi.registerTool({
37
- name: "browser_trace_start",
38
- label: "Browser Trace Start",
39
- description: "Start a Playwright trace for the current browser session and persist trace metadata under the session artifact directory.",
30
+ name: "browser_trace",
31
+ label: "Browser Trace",
32
+ description: "Manage Playwright tracing and HAR export: start/stop traces, export session HAR. " +
33
+ "Traces capture screenshots, snapshots, and sources for debugging.",
40
34
  parameters: Type.Object({
41
- name: Type.Optional(Type.String({ description: "Optional short trace session name for artifact filenames." })),
42
- title: Type.Optional(Type.String({ description: "Optional trace title recorded in metadata." })),
35
+ action: Type.Union([
36
+ Type.Literal("start"),
37
+ Type.Literal("stop"),
38
+ Type.Literal("export_har"),
39
+ ], { description: "'start' — begin trace, 'stop' — end trace and save, 'export_har' — export network HAR" }),
40
+ name: Type.Optional(Type.String({ description: "Name for the trace file or HAR export." })),
41
+ title: Type.Optional(Type.String({ description: "Trace title (start action only)." })),
43
42
  }),
44
43
  async execute(_toolCallId, params, _signal, _onUpdate, _ctx) {
45
44
  try {
46
- const { context: browserContext } = await deps.ensureBrowser();
47
- const activeTrace = getActiveTraceSession();
48
- if (activeTrace) {
49
- return {
50
- content: [{ type: "text", text: `Trace already active: ${activeTrace.name}` }],
51
- details: { error: "trace_already_active", activeTraceSession: activeTrace, ...deps.getSessionArtifactMetadata() },
52
- isError: true,
53
- };
45
+ if (params.action === "start") {
46
+ return await traceStart(params);
54
47
  }
55
- const startedAt = Date.now();
56
- const name = (params.name?.trim() || `trace-${deps.formatArtifactTimestamp(startedAt)}`).replace(/[^a-zA-Z0-9._-]+/g, "-");
57
- await browserContext.tracing.start({ screenshots: true, snapshots: true, sources: true, title: params.title ?? name });
58
- setActiveTraceSession({ startedAt, name, title: params.title ?? name });
59
- return {
60
- content: [{ type: "text", text: `Trace started: ${name}\nSession dir: ${getSessionArtifactDir()}` }],
61
- details: { activeTraceSession: getActiveTraceSession(), ...deps.getSessionArtifactMetadata() },
62
- };
63
- }
64
- catch (err) {
65
- return {
66
- content: [{ type: "text", text: `Trace start failed: ${err.message}` }],
67
- details: { error: err.message, ...deps.getSessionArtifactMetadata() },
68
- isError: true,
69
- };
70
- }
71
- },
72
- });
73
- // -------------------------------------------------------------------------
74
- // browser_trace_stop
75
- // -------------------------------------------------------------------------
76
- pi.registerTool({
77
- name: "browser_trace_stop",
78
- label: "Browser Trace Stop",
79
- description: "Stop the active Playwright trace and write the trace zip to disk under the session artifact directory.",
80
- parameters: Type.Object({
81
- name: Type.Optional(Type.String({ description: "Optional artifact basename override for the trace zip." })),
82
- }),
83
- async execute(_toolCallId, params, _signal, _onUpdate, _ctx) {
84
- try {
85
- const { context: browserContext } = await deps.ensureBrowser();
86
- const activeTrace = getActiveTraceSession();
87
- if (!activeTrace) {
88
- return {
89
- content: [{ type: "text", text: "No active trace session to stop." }],
90
- details: { error: "trace_not_active", ...deps.getSessionArtifactMetadata() },
91
- isError: true,
92
- };
48
+ else if (params.action === "stop") {
49
+ return await traceStop(params);
93
50
  }
94
- const traceSession = activeTrace;
95
- const traceName = (params.name?.trim() || traceSession.name).replace(/[^a-zA-Z0-9._-]+/g, "-");
96
- const tracePath = deps.buildSessionArtifactPath(`${traceName}.trace.zip`);
97
- await browserContext.tracing.stop({ path: tracePath });
98
- const fileStat = await stat(tracePath);
99
- setActiveTraceSession(null);
100
- return {
101
- content: [{ type: "text", text: `Trace stopped: ${tracePath}` }],
102
- details: {
103
- path: tracePath,
104
- bytes: fileStat.size,
105
- elapsedMs: Date.now() - traceSession.startedAt,
106
- traceName,
107
- ...deps.getSessionArtifactMetadata(),
108
- },
109
- };
110
- }
111
- catch (err) {
112
- return {
113
- content: [{ type: "text", text: `Trace stop failed: ${err.message}` }],
114
- details: { error: err.message, ...deps.getSessionArtifactMetadata() },
115
- isError: true,
116
- };
117
- }
118
- },
119
- });
120
- // -------------------------------------------------------------------------
121
- // browser_export_har
122
- // -------------------------------------------------------------------------
123
- pi.registerTool({
124
- name: "browser_export_har",
125
- label: "Browser Export HAR",
126
- description: "Export the truthfully recorded session HAR from disk to a stable artifact path and return compact metadata.",
127
- parameters: Type.Object({
128
- filename: Type.Optional(Type.String({ description: "Optional destination filename within the session artifact directory." })),
129
- }),
130
- async execute(_toolCallId, params, _signal, _onUpdate, _ctx) {
131
- try {
132
- await deps.ensureBrowser();
133
- const harState = getHarState();
134
- if (!harState.enabled || !harState.configuredAtContextCreation || !harState.path) {
135
- return {
136
- content: [{ type: "text", text: "HAR export unavailable: HAR recording was not enabled at browser context creation." }],
137
- details: { error: "har_not_enabled", ...deps.getSessionArtifactMetadata() },
138
- isError: true,
139
- };
51
+ else {
52
+ return await exportHar({ filename: params.name });
140
53
  }
141
- const sourcePath = harState.path;
142
- const destinationName = (params.filename?.trim() || `export-${HAR_FILENAME}`).replace(/[^a-zA-Z0-9._-]+/g, "-");
143
- const destinationPath = deps.buildSessionArtifactPath(destinationName);
144
- const exportResult = sourcePath === destinationPath
145
- ? { path: sourcePath, bytes: (await stat(sourcePath)).size }
146
- : await deps.copyArtifactFile(sourcePath, destinationPath);
147
- setHarState({
148
- ...harState,
149
- exportCount: harState.exportCount + 1,
150
- lastExportedPath: exportResult.path,
151
- lastExportedAt: Date.now(),
152
- });
153
- return {
154
- content: [{ type: "text", text: `HAR exported: ${exportResult.path}` }],
155
- details: { path: exportResult.path, bytes: exportResult.bytes, ...deps.getSessionArtifactMetadata() },
156
- };
157
54
  }
158
55
  catch (err) {
159
- return {
160
- content: [{ type: "text", text: `HAR export failed: ${err.message}` }],
161
- details: { error: err.message, ...deps.getSessionArtifactMetadata() },
162
- isError: true,
163
- };
56
+ return { content: [{ type: "text", text: `Trace '${params.action}' failed: ${err.message}` }], details: { error: err.message, ...deps.getSessionArtifactMetadata() }, isError: true };
164
57
  }
165
58
  },
166
59
  });
167
60
  // -------------------------------------------------------------------------
168
- // browser_timeline
61
+ // browser_debug — timeline, session summary, debug bundle
169
62
  // -------------------------------------------------------------------------
170
63
  pi.registerTool({
171
- name: "browser_timeline",
172
- label: "Browser Timeline",
173
- description: "Return a compact structured summary of the tracked browser action timeline and optional on-disk export path.",
64
+ name: "browser_debug",
65
+ label: "Browser Debug",
66
+ description: "Browser session introspection: view action timeline, session summary, or write a full debug bundle to disk. " +
67
+ "Use for debugging failing tests or complex browser interactions.",
174
68
  parameters: Type.Object({
175
- writeToDisk: Type.Optional(Type.Boolean({ description: "Write the timeline JSON to disk under the session artifact directory." })),
176
- filename: Type.Optional(Type.String({ description: "Optional JSON filename when writeToDisk is true." })),
69
+ action: Type.Union([
70
+ Type.Literal("timeline"),
71
+ Type.Literal("summary"),
72
+ Type.Literal("bundle"),
73
+ ], { description: "'timeline' — action history, 'summary' — session overview, 'bundle' — write full debug bundle to disk" }),
74
+ writeToDisk: Type.Optional(Type.Boolean({ description: "Write timeline JSON to disk (timeline action)." })),
75
+ filename: Type.Optional(Type.String({ description: "Filename for timeline/bundle output." })),
76
+ selector: Type.Optional(Type.String({ description: "CSS selector scope for accessibility snapshot (bundle action)." })),
77
+ name: Type.Optional(Type.String({ description: "Bundle name suffix (bundle action)." })),
177
78
  }),
178
79
  async execute(_toolCallId, params, _signal, _onUpdate, _ctx) {
179
80
  try {
180
- await deps.ensureBrowser();
181
- const actionTimeline = getActionTimeline();
182
- const timeline = formatTimelineEntries(actionTimeline.entries, {
183
- limit: actionTimeline.limit,
184
- totalActions: actionTimeline.nextId - 1,
185
- });
186
- let artifact = null;
187
- if (params.writeToDisk) {
188
- const filename = (params.filename?.trim() || "timeline.json").replace(/[^a-zA-Z0-9._-]+/g, "-");
189
- artifact = await deps.writeArtifactFile(deps.buildSessionArtifactPath(filename), JSON.stringify(timeline, null, 2));
81
+ if (params.action === "timeline") {
82
+ return await timelineAction(params);
83
+ }
84
+ else if (params.action === "summary") {
85
+ return await summaryAction();
86
+ }
87
+ else {
88
+ return await bundleAction(params);
190
89
  }
191
- return {
192
- content: [{ type: "text", text: artifact ? `${timeline.summary}\nArtifact: ${artifact.path}` : timeline.summary }],
193
- details: { ...timeline, artifact, ...deps.getSessionArtifactMetadata() },
194
- };
195
- }
196
- catch (err) {
197
- return {
198
- content: [{ type: "text", text: `Timeline failed: ${err.message}` }],
199
- details: { error: err.message, ...deps.getSessionArtifactMetadata() },
200
- isError: true,
201
- };
202
- }
203
- },
204
- });
205
- // -------------------------------------------------------------------------
206
- // browser_session_summary
207
- // -------------------------------------------------------------------------
208
- pi.registerTool({
209
- name: "browser_session_summary",
210
- label: "Browser Session Summary",
211
- description: "Return a compact structured summary of the current browser session, including pages, actions, waits/assertions, bounded-history caveats, and trace/HAR state.",
212
- parameters: Type.Object({}),
213
- async execute(_toolCallId, _params, _signal, _onUpdate, _ctx) {
214
- try {
215
- await deps.ensureBrowser();
216
- const pages = await deps.getLivePagesSnapshot();
217
- const actionTimeline = getActionTimeline();
218
- const pageRegistry = getPageRegistry();
219
- const consoleLogs = getConsoleLogs();
220
- const networkLogs = getNetworkLogs();
221
- const dialogLogs = getDialogLogs();
222
- const baseSummary = summarizeBrowserSession({
223
- timeline: actionTimeline,
224
- totalActions: actionTimeline.nextId - 1,
225
- pages,
226
- activePageId: pageRegistry.activePageId,
227
- activeFrame: getActiveFrameMetadata(),
228
- consoleEntries: consoleLogs,
229
- networkEntries: networkLogs,
230
- dialogEntries: dialogLogs,
231
- consoleLimit: 1000,
232
- networkLimit: 1000,
233
- dialogLimit: 1000,
234
- sessionStartedAt: getSessionStartedAt(),
235
- now: Date.now(),
236
- });
237
- const failureHypothesis = buildFailureHypothesis({
238
- timeline: actionTimeline,
239
- consoleEntries: consoleLogs,
240
- networkEntries: networkLogs,
241
- dialogEntries: dialogLogs,
242
- });
243
- const activeTrace = getActiveTraceSession();
244
- const traceState = activeTrace
245
- ? { status: "active", ...activeTrace }
246
- : { status: "inactive", lastTracePath: getSessionArtifactDir() ? deps.buildSessionArtifactPath("*.trace.zip") : null };
247
- const harState = getHarState();
248
- const harSummary = {
249
- enabled: harState.enabled,
250
- configuredAtContextCreation: harState.configuredAtContextCreation,
251
- path: harState.path,
252
- exportCount: harState.exportCount,
253
- lastExportedPath: harState.lastExportedPath,
254
- lastExportedAt: harState.lastExportedAt,
255
- };
256
- return {
257
- content: [{ type: "text", text: `${baseSummary.summary}\nFailure hypothesis: ${failureHypothesis}` }],
258
- details: {
259
- ...baseSummary,
260
- failureHypothesis,
261
- trace: traceState,
262
- har: harSummary,
263
- ...deps.getSessionArtifactMetadata(),
264
- },
265
- };
266
- }
267
- catch (err) {
268
- return {
269
- content: [{ type: "text", text: `Session summary failed: ${err.message}` }],
270
- details: { error: err.message, ...deps.getSessionArtifactMetadata() },
271
- isError: true,
272
- };
273
- }
274
- },
275
- });
276
- // -------------------------------------------------------------------------
277
- // browser_debug_bundle
278
- // -------------------------------------------------------------------------
279
- pi.registerTool({
280
- name: "browser_debug_bundle",
281
- label: "Browser Debug Bundle",
282
- description: "Write a timestamped debug bundle to disk with screenshot, logs, timeline, pages, session summary, and accessibility output, then return compact paths and counts.",
283
- parameters: Type.Object({
284
- selector: Type.Optional(Type.String({ description: "Optional CSS selector to scope the accessibility snapshot before fallback behavior applies." })),
285
- name: Type.Optional(Type.String({ description: "Optional short bundle name suffix for the output directory." })),
286
- }),
287
- async execute(_toolCallId, params, _signal, _onUpdate, _ctx) {
288
- try {
289
- const { page: p } = await deps.ensureBrowser();
290
- const startedAt = Date.now();
291
- const sessionDir = await deps.ensureSessionArtifactDir();
292
- const bundleDir = path.join(ARTIFACT_ROOT, `${deps.formatArtifactTimestamp(startedAt)}-${deps.sanitizeArtifactName(params.name ?? "debug-bundle", "debug-bundle")}`);
293
- await ensureDir(bundleDir);
294
- const pages = await deps.getLivePagesSnapshot();
295
- const actionTimeline = getActionTimeline();
296
- const pageRegistry = getPageRegistry();
297
- const consoleLogs = getConsoleLogs();
298
- const networkLogs = getNetworkLogs();
299
- const dialogLogs = getDialogLogs();
300
- const timeline = formatTimelineEntries(actionTimeline.entries, {
301
- limit: actionTimeline.limit,
302
- totalActions: actionTimeline.nextId - 1,
303
- });
304
- const sessionSummary = summarizeBrowserSession({
305
- timeline: actionTimeline,
306
- totalActions: actionTimeline.nextId - 1,
307
- pages,
308
- activePageId: pageRegistry.activePageId,
309
- activeFrame: getActiveFrameMetadata(),
310
- consoleEntries: consoleLogs,
311
- networkEntries: networkLogs,
312
- dialogEntries: dialogLogs,
313
- consoleLimit: 1000,
314
- networkLimit: 1000,
315
- dialogLimit: 1000,
316
- sessionStartedAt: getSessionStartedAt(),
317
- now: Date.now(),
318
- });
319
- const failureHypothesis = buildFailureHypothesis({
320
- timeline: actionTimeline,
321
- consoleEntries: consoleLogs,
322
- networkEntries: networkLogs,
323
- dialogEntries: dialogLogs,
324
- });
325
- const accessibility = await deps.captureAccessibilityMarkdown(params.selector);
326
- const screenshotPath = path.join(bundleDir, "screenshot.jpg");
327
- await p.screenshot({ path: screenshotPath, type: "jpeg", quality: 80, fullPage: false });
328
- const screenshotStat = await stat(screenshotPath);
329
- const artifacts = {
330
- screenshot: { path: screenshotPath, bytes: screenshotStat.size },
331
- console: await deps.writeArtifactFile(path.join(bundleDir, "console.json"), JSON.stringify(consoleLogs, null, 2)),
332
- network: await deps.writeArtifactFile(path.join(bundleDir, "network.json"), JSON.stringify(networkLogs, null, 2)),
333
- dialog: await deps.writeArtifactFile(path.join(bundleDir, "dialog.json"), JSON.stringify(dialogLogs, null, 2)),
334
- timeline: await deps.writeArtifactFile(path.join(bundleDir, "timeline.json"), JSON.stringify(timeline, null, 2)),
335
- summary: await deps.writeArtifactFile(path.join(bundleDir, "summary.json"), JSON.stringify({
336
- ...sessionSummary,
337
- failureHypothesis,
338
- trace: getActiveTraceSession(),
339
- har: getHarState(),
340
- sessionArtifactDir: sessionDir,
341
- }, null, 2)),
342
- pages: await deps.writeArtifactFile(path.join(bundleDir, "pages.json"), JSON.stringify(pages, null, 2)),
343
- accessibility: await deps.writeArtifactFile(path.join(bundleDir, "accessibility.md"), accessibility.snapshot),
344
- };
345
- return {
346
- content: [{ type: "text", text: `Debug bundle written: ${bundleDir}\n${sessionSummary.summary}\nFailure hypothesis: ${failureHypothesis}` }],
347
- details: {
348
- bundleDir,
349
- artifacts,
350
- accessibilityScope: accessibility.scope,
351
- accessibilitySource: accessibility.source,
352
- counts: {
353
- console: consoleLogs.length,
354
- network: networkLogs.length,
355
- dialog: dialogLogs.length,
356
- actions: timeline.retained,
357
- pages: pages.length,
358
- },
359
- elapsedMs: Date.now() - startedAt,
360
- summary: sessionSummary,
361
- failureHypothesis,
362
- ...deps.getSessionArtifactMetadata(),
363
- },
364
- };
365
90
  }
366
91
  catch (err) {
367
- return {
368
- content: [{ type: "text", text: `Debug bundle failed: ${err.message}` }],
369
- details: { error: err.message, ...deps.getSessionArtifactMetadata() },
370
- isError: true,
371
- };
92
+ return { content: [{ type: "text", text: `Debug '${params.action}' failed: ${err.message}` }], details: { error: err.message, ...deps.getSessionArtifactMetadata() }, isError: true };
372
93
  }
373
94
  },
374
95
  });
96
+ // ── trace helpers ──
97
+ async function traceStart(params) {
98
+ const { context: browserContext } = await deps.ensureBrowser();
99
+ const activeTrace = getActiveTraceSession();
100
+ if (activeTrace) {
101
+ return { content: [{ type: "text", text: `Trace already active: ${activeTrace.name}` }], details: { error: "trace_already_active", ...deps.getSessionArtifactMetadata() }, isError: true };
102
+ }
103
+ const startedAt = Date.now();
104
+ const name = (params.name?.trim() || `trace-${deps.formatArtifactTimestamp(startedAt)}`).replace(/[^a-zA-Z0-9._-]+/g, "-");
105
+ await browserContext.tracing.start({ screenshots: true, snapshots: true, sources: true, title: params.title ?? name });
106
+ setActiveTraceSession({ startedAt, name, title: params.title ?? name });
107
+ return {
108
+ content: [{ type: "text", text: `Trace started: ${name}\nSession dir: ${getSessionArtifactDir()}` }],
109
+ details: { activeTraceSession: getActiveTraceSession(), ...deps.getSessionArtifactMetadata() },
110
+ };
111
+ }
112
+ async function traceStop(params) {
113
+ const { context: browserContext } = await deps.ensureBrowser();
114
+ const activeTrace = getActiveTraceSession();
115
+ if (!activeTrace) {
116
+ return { content: [{ type: "text", text: "No active trace session." }], details: { error: "trace_not_active", ...deps.getSessionArtifactMetadata() }, isError: true };
117
+ }
118
+ const traceName = (params.name?.trim() || activeTrace.name).replace(/[^a-zA-Z0-9._-]+/g, "-");
119
+ const tracePath = deps.buildSessionArtifactPath(`${traceName}.trace.zip`);
120
+ await browserContext.tracing.stop({ path: tracePath });
121
+ const fileStat = await stat(tracePath);
122
+ setActiveTraceSession(null);
123
+ return {
124
+ content: [{ type: "text", text: `Trace stopped: ${tracePath}` }],
125
+ details: { path: tracePath, bytes: fileStat.size, elapsedMs: Date.now() - activeTrace.startedAt, traceName, ...deps.getSessionArtifactMetadata() },
126
+ };
127
+ }
128
+ async function exportHar(params) {
129
+ await deps.ensureBrowser();
130
+ const harState = getHarState();
131
+ if (!harState.enabled || !harState.configuredAtContextCreation || !harState.path) {
132
+ return { content: [{ type: "text", text: "HAR export unavailable: HAR recording was not enabled at context creation." }], details: { error: "har_not_enabled", ...deps.getSessionArtifactMetadata() }, isError: true };
133
+ }
134
+ const destinationName = (params.filename?.trim() || `export-${HAR_FILENAME}`).replace(/[^a-zA-Z0-9._-]+/g, "-");
135
+ const destinationPath = deps.buildSessionArtifactPath(destinationName);
136
+ const exportResult = harState.path === destinationPath
137
+ ? { path: harState.path, bytes: (await stat(harState.path)).size }
138
+ : await deps.copyArtifactFile(harState.path, destinationPath);
139
+ setHarState({ ...harState, exportCount: harState.exportCount + 1, lastExportedPath: exportResult.path, lastExportedAt: Date.now() });
140
+ return {
141
+ content: [{ type: "text", text: `HAR exported: ${exportResult.path}` }],
142
+ details: { path: exportResult.path, bytes: exportResult.bytes, ...deps.getSessionArtifactMetadata() },
143
+ };
144
+ }
145
+ // ── debug helpers ──
146
+ async function timelineAction(params) {
147
+ await deps.ensureBrowser();
148
+ const actionTimeline = getActionTimeline();
149
+ const timeline = formatTimelineEntries(actionTimeline.entries, { limit: actionTimeline.limit, totalActions: actionTimeline.nextId - 1 });
150
+ let artifact = null;
151
+ if (params.writeToDisk) {
152
+ const filename = (params.filename?.trim() || "timeline.json").replace(/[^a-zA-Z0-9._-]+/g, "-");
153
+ artifact = await deps.writeArtifactFile(deps.buildSessionArtifactPath(filename), JSON.stringify(timeline, null, 2));
154
+ }
155
+ return {
156
+ content: [{ type: "text", text: artifact ? `${timeline.summary}\nArtifact: ${artifact.path}` : timeline.summary }],
157
+ details: { ...timeline, artifact, ...deps.getSessionArtifactMetadata() },
158
+ };
159
+ }
160
+ async function summaryAction() {
161
+ await deps.ensureBrowser();
162
+ const pages = await deps.getLivePagesSnapshot();
163
+ const actionTimeline = getActionTimeline();
164
+ const pageRegistry = getPageRegistry();
165
+ const consoleLogs = getConsoleLogs();
166
+ const networkLogs = getNetworkLogs();
167
+ const dialogLogs = getDialogLogs();
168
+ const baseSummary = summarizeBrowserSession({
169
+ timeline: actionTimeline, totalActions: actionTimeline.nextId - 1, pages,
170
+ activePageId: pageRegistry.activePageId, activeFrame: getActiveFrameMetadata(),
171
+ consoleEntries: consoleLogs, networkEntries: networkLogs, dialogEntries: dialogLogs,
172
+ consoleLimit: 1000, networkLimit: 1000, dialogLimit: 1000,
173
+ sessionStartedAt: getSessionStartedAt(), now: Date.now(),
174
+ });
175
+ const failureHypothesis = buildFailureHypothesis({ timeline: actionTimeline, consoleEntries: consoleLogs, networkEntries: networkLogs, dialogEntries: dialogLogs });
176
+ const activeTrace = getActiveTraceSession();
177
+ const traceState = activeTrace ? { status: "active", ...activeTrace } : { status: "inactive", lastTracePath: getSessionArtifactDir() ? deps.buildSessionArtifactPath("*.trace.zip") : null };
178
+ const harState = getHarState();
179
+ return {
180
+ content: [{ type: "text", text: `${baseSummary.summary}\nFailure hypothesis: ${failureHypothesis}` }],
181
+ details: { ...baseSummary, failureHypothesis, trace: traceState, har: { enabled: harState.enabled, exportCount: harState.exportCount }, ...deps.getSessionArtifactMetadata() },
182
+ };
183
+ }
184
+ async function bundleAction(params) {
185
+ const { page: p } = await deps.ensureBrowser();
186
+ const startedAt = Date.now();
187
+ const sessionDir = await deps.ensureSessionArtifactDir();
188
+ const bundleDir = path.join(ARTIFACT_ROOT, `${deps.formatArtifactTimestamp(startedAt)}-${deps.sanitizeArtifactName(params.name ?? "debug-bundle", "debug-bundle")}`);
189
+ await ensureDir(bundleDir);
190
+ const pages = await deps.getLivePagesSnapshot();
191
+ const actionTimeline = getActionTimeline();
192
+ const pageRegistry = getPageRegistry();
193
+ const consoleLogs = getConsoleLogs();
194
+ const networkLogs = getNetworkLogs();
195
+ const dialogLogs = getDialogLogs();
196
+ const timeline = formatTimelineEntries(actionTimeline.entries, { limit: actionTimeline.limit, totalActions: actionTimeline.nextId - 1 });
197
+ const sessionSummary = summarizeBrowserSession({
198
+ timeline: actionTimeline, totalActions: actionTimeline.nextId - 1, pages,
199
+ activePageId: pageRegistry.activePageId, activeFrame: getActiveFrameMetadata(),
200
+ consoleEntries: consoleLogs, networkEntries: networkLogs, dialogEntries: dialogLogs,
201
+ consoleLimit: 1000, networkLimit: 1000, dialogLimit: 1000,
202
+ sessionStartedAt: getSessionStartedAt(), now: Date.now(),
203
+ });
204
+ const failureHypothesis = buildFailureHypothesis({ timeline: actionTimeline, consoleEntries: consoleLogs, networkEntries: networkLogs, dialogEntries: dialogLogs });
205
+ const accessibility = await deps.captureAccessibilityMarkdown(params.selector);
206
+ const screenshotPath = path.join(bundleDir, "screenshot.jpg");
207
+ await p.screenshot({ path: screenshotPath, type: "jpeg", quality: 80, fullPage: false });
208
+ const screenshotStat = await stat(screenshotPath);
209
+ const artifacts = {
210
+ screenshot: { path: screenshotPath, bytes: screenshotStat.size },
211
+ console: await deps.writeArtifactFile(path.join(bundleDir, "console.json"), JSON.stringify(consoleLogs, null, 2)),
212
+ network: await deps.writeArtifactFile(path.join(bundleDir, "network.json"), JSON.stringify(networkLogs, null, 2)),
213
+ dialog: await deps.writeArtifactFile(path.join(bundleDir, "dialog.json"), JSON.stringify(dialogLogs, null, 2)),
214
+ timeline: await deps.writeArtifactFile(path.join(bundleDir, "timeline.json"), JSON.stringify(timeline, null, 2)),
215
+ summary: await deps.writeArtifactFile(path.join(bundleDir, "summary.json"), JSON.stringify({ ...sessionSummary, failureHypothesis, trace: getActiveTraceSession(), sessionArtifactDir: sessionDir }, null, 2)),
216
+ pages: await deps.writeArtifactFile(path.join(bundleDir, "pages.json"), JSON.stringify(pages, null, 2)),
217
+ accessibility: await deps.writeArtifactFile(path.join(bundleDir, "accessibility.md"), accessibility.snapshot),
218
+ };
219
+ return {
220
+ content: [{ type: "text", text: `Debug bundle written: ${bundleDir}\n${sessionSummary.summary}\nFailure hypothesis: ${failureHypothesis}` }],
221
+ details: {
222
+ bundleDir, artifacts, accessibilityScope: accessibility.scope, accessibilitySource: accessibility.source,
223
+ counts: { console: consoleLogs.length, network: networkLogs.length, dialog: dialogLogs.length, actions: timeline.retained, pages: pages.length },
224
+ elapsedMs: Date.now() - startedAt, summary: sessionSummary, failureHypothesis, ...deps.getSessionArtifactMetadata(),
225
+ },
226
+ };
227
+ }
375
228
  }