sparkecoder 0.1.69 → 0.1.70

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 (115) hide show
  1. package/dist/agent/index.d.ts +3 -3
  2. package/dist/agent/index.js +11 -4
  3. package/dist/agent/index.js.map +1 -1
  4. package/dist/cli.js +75 -62
  5. package/dist/cli.js.map +1 -1
  6. package/dist/db/index.d.ts +2 -2
  7. package/dist/{index-DqaHLgSC.d.ts → index-Dm6wGcYv.d.ts} +19 -19
  8. package/dist/index.d.ts +5 -5
  9. package/dist/index.js +75 -62
  10. package/dist/index.js.map +1 -1
  11. package/dist/{schema-Bq4tID-f.d.ts → schema-XcP0dedO.d.ts} +3 -3
  12. package/dist/{search-BRnGaIl-.d.ts → search-CCffrVJE.d.ts} +7 -7
  13. package/dist/server/index.js +75 -62
  14. package/dist/server/index.js.map +1 -1
  15. package/dist/skills/default/qa.md +376 -106
  16. package/dist/tools/index.d.ts +2 -2
  17. package/package.json +1 -1
  18. package/src/skills/default/qa.md +376 -106
  19. package/web/.next/BUILD_ID +1 -1
  20. package/web/.next/standalone/web/.next/BUILD_ID +1 -1
  21. package/web/.next/standalone/web/.next/build-manifest.json +2 -2
  22. package/web/.next/standalone/web/.next/prerender-manifest.json +3 -3
  23. package/web/.next/standalone/web/.next/server/app/(main)/page_client-reference-manifest.js +1 -1
  24. package/web/.next/standalone/web/.next/server/app/(main)/session/[id]/page.js.nft.json +1 -1
  25. package/web/.next/standalone/web/.next/server/app/(main)/session/[id]/page_client-reference-manifest.js +1 -1
  26. package/web/.next/standalone/web/.next/server/app/_global-error.html +2 -2
  27. package/web/.next/standalone/web/.next/server/app/_global-error.rsc +1 -1
  28. package/web/.next/standalone/web/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +1 -1
  29. package/web/.next/standalone/web/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  30. package/web/.next/standalone/web/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  31. package/web/.next/standalone/web/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  32. package/web/.next/standalone/web/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  33. package/web/.next/standalone/web/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  34. package/web/.next/standalone/web/.next/server/app/_not-found.html +1 -1
  35. package/web/.next/standalone/web/.next/server/app/_not-found.rsc +2 -2
  36. package/web/.next/standalone/web/.next/server/app/_not-found.segments/_full.segment.rsc +2 -2
  37. package/web/.next/standalone/web/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
  38. package/web/.next/standalone/web/.next/server/app/_not-found.segments/_index.segment.rsc +2 -2
  39. package/web/.next/standalone/web/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
  40. package/web/.next/standalone/web/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
  41. package/web/.next/standalone/web/.next/server/app/_not-found.segments/_tree.segment.rsc +2 -2
  42. package/web/.next/standalone/web/.next/server/app/docs/installation/page_client-reference-manifest.js +1 -1
  43. package/web/.next/standalone/web/.next/server/app/docs/installation.html +2 -2
  44. package/web/.next/standalone/web/.next/server/app/docs/installation.rsc +2 -2
  45. package/web/.next/standalone/web/.next/server/app/docs/installation.segments/_full.segment.rsc +2 -2
  46. package/web/.next/standalone/web/.next/server/app/docs/installation.segments/_head.segment.rsc +1 -1
  47. package/web/.next/standalone/web/.next/server/app/docs/installation.segments/_index.segment.rsc +2 -2
  48. package/web/.next/standalone/web/.next/server/app/docs/installation.segments/_tree.segment.rsc +2 -2
  49. package/web/.next/standalone/web/.next/server/app/docs/installation.segments/docs/installation/__PAGE__.segment.rsc +1 -1
  50. package/web/.next/standalone/web/.next/server/app/docs/installation.segments/docs/installation.segment.rsc +1 -1
  51. package/web/.next/standalone/web/.next/server/app/docs/installation.segments/docs.segment.rsc +1 -1
  52. package/web/.next/standalone/web/.next/server/app/docs/page_client-reference-manifest.js +1 -1
  53. package/web/.next/standalone/web/.next/server/app/docs/skills/page_client-reference-manifest.js +1 -1
  54. package/web/.next/standalone/web/.next/server/app/docs/skills.html +2 -2
  55. package/web/.next/standalone/web/.next/server/app/docs/skills.rsc +2 -2
  56. package/web/.next/standalone/web/.next/server/app/docs/skills.segments/_full.segment.rsc +2 -2
  57. package/web/.next/standalone/web/.next/server/app/docs/skills.segments/_head.segment.rsc +1 -1
  58. package/web/.next/standalone/web/.next/server/app/docs/skills.segments/_index.segment.rsc +2 -2
  59. package/web/.next/standalone/web/.next/server/app/docs/skills.segments/_tree.segment.rsc +2 -2
  60. package/web/.next/standalone/web/.next/server/app/docs/skills.segments/docs/skills/__PAGE__.segment.rsc +1 -1
  61. package/web/.next/standalone/web/.next/server/app/docs/skills.segments/docs/skills.segment.rsc +1 -1
  62. package/web/.next/standalone/web/.next/server/app/docs/skills.segments/docs.segment.rsc +1 -1
  63. package/web/.next/standalone/web/.next/server/app/docs/tools/page_client-reference-manifest.js +1 -1
  64. package/web/.next/standalone/web/.next/server/app/docs/tools.html +2 -2
  65. package/web/.next/standalone/web/.next/server/app/docs/tools.rsc +2 -2
  66. package/web/.next/standalone/web/.next/server/app/docs/tools.segments/_full.segment.rsc +2 -2
  67. package/web/.next/standalone/web/.next/server/app/docs/tools.segments/_head.segment.rsc +1 -1
  68. package/web/.next/standalone/web/.next/server/app/docs/tools.segments/_index.segment.rsc +2 -2
  69. package/web/.next/standalone/web/.next/server/app/docs/tools.segments/_tree.segment.rsc +2 -2
  70. package/web/.next/standalone/web/.next/server/app/docs/tools.segments/docs/tools/__PAGE__.segment.rsc +1 -1
  71. package/web/.next/standalone/web/.next/server/app/docs/tools.segments/docs/tools.segment.rsc +1 -1
  72. package/web/.next/standalone/web/.next/server/app/docs/tools.segments/docs.segment.rsc +1 -1
  73. package/web/.next/standalone/web/.next/server/app/docs.html +2 -2
  74. package/web/.next/standalone/web/.next/server/app/docs.rsc +2 -2
  75. package/web/.next/standalone/web/.next/server/app/docs.segments/_full.segment.rsc +2 -2
  76. package/web/.next/standalone/web/.next/server/app/docs.segments/_head.segment.rsc +1 -1
  77. package/web/.next/standalone/web/.next/server/app/docs.segments/_index.segment.rsc +2 -2
  78. package/web/.next/standalone/web/.next/server/app/docs.segments/_tree.segment.rsc +2 -2
  79. package/web/.next/standalone/web/.next/server/app/docs.segments/docs/__PAGE__.segment.rsc +1 -1
  80. package/web/.next/standalone/web/.next/server/app/docs.segments/docs.segment.rsc +1 -1
  81. package/web/.next/standalone/web/.next/server/app/embed/[id]/page.js.nft.json +1 -1
  82. package/web/.next/standalone/web/.next/server/app/embed/[id]/page_client-reference-manifest.js +1 -1
  83. package/web/.next/standalone/web/.next/server/app/index.html +1 -1
  84. package/web/.next/standalone/web/.next/server/app/index.rsc +2 -2
  85. package/web/.next/standalone/web/.next/server/app/index.segments/!KG1haW4p/__PAGE__.segment.rsc +1 -1
  86. package/web/.next/standalone/web/.next/server/app/index.segments/!KG1haW4p.segment.rsc +1 -1
  87. package/web/.next/standalone/web/.next/server/app/index.segments/_full.segment.rsc +2 -2
  88. package/web/.next/standalone/web/.next/server/app/index.segments/_head.segment.rsc +1 -1
  89. package/web/.next/standalone/web/.next/server/app/index.segments/_index.segment.rsc +2 -2
  90. package/web/.next/standalone/web/.next/server/app/index.segments/_tree.segment.rsc +2 -2
  91. package/web/.next/standalone/web/.next/server/chunks/ssr/{[root-of-the-server]__c71f29f9._.js → [root-of-the-server]__a1877334._.js} +4 -4
  92. package/web/.next/standalone/web/.next/server/pages/404.html +1 -1
  93. package/web/.next/standalone/web/.next/server/pages/500.html +2 -2
  94. package/web/.next/standalone/web/.next/server/server-reference-manifest.js +1 -1
  95. package/web/.next/standalone/web/.next/server/server-reference-manifest.json +1 -1
  96. package/web/.next/standalone/web/.next/static/chunks/41a5c049931b2c77.css +1 -0
  97. package/web/.next/standalone/web/.next/static/chunks/{b6ec74cad9ffd3ee.js → f95d41079838994a.js} +3 -3
  98. package/web/.next/standalone/web/.next/static/static/chunks/41a5c049931b2c77.css +1 -0
  99. package/web/.next/standalone/web/.next/static/static/chunks/{b6ec74cad9ffd3ee.js → f95d41079838994a.js} +3 -3
  100. package/web/.next/standalone/web/src/components/ai-elements/read-file-tool.tsx +19 -2
  101. package/web/.next/standalone/web/src/components/chat-interface.tsx +49 -4
  102. package/web/.next/static/chunks/41a5c049931b2c77.css +1 -0
  103. package/web/.next/static/chunks/{b6ec74cad9ffd3ee.js → f95d41079838994a.js} +3 -3
  104. package/web/.next/standalone/web/.next/static/chunks/fd39dd62879495e1.css +0 -1
  105. package/web/.next/standalone/web/.next/static/static/chunks/fd39dd62879495e1.css +0 -1
  106. package/web/.next/static/chunks/fd39dd62879495e1.css +0 -1
  107. /package/web/.next/standalone/web/.next/static/{XB638PEDChQhwk6wSMrSh → PpaOWDfndYJrA-tJYr7gU}/_buildManifest.js +0 -0
  108. /package/web/.next/standalone/web/.next/static/{XB638PEDChQhwk6wSMrSh → PpaOWDfndYJrA-tJYr7gU}/_clientMiddlewareManifest.json +0 -0
  109. /package/web/.next/standalone/web/.next/static/{XB638PEDChQhwk6wSMrSh → PpaOWDfndYJrA-tJYr7gU}/_ssgManifest.js +0 -0
  110. /package/web/.next/standalone/web/.next/static/static/{XB638PEDChQhwk6wSMrSh → PpaOWDfndYJrA-tJYr7gU}/_buildManifest.js +0 -0
  111. /package/web/.next/standalone/web/.next/static/static/{XB638PEDChQhwk6wSMrSh → PpaOWDfndYJrA-tJYr7gU}/_clientMiddlewareManifest.json +0 -0
  112. /package/web/.next/standalone/web/.next/static/static/{XB638PEDChQhwk6wSMrSh → PpaOWDfndYJrA-tJYr7gU}/_ssgManifest.js +0 -0
  113. /package/web/.next/static/{XB638PEDChQhwk6wSMrSh → PpaOWDfndYJrA-tJYr7gU}/_buildManifest.js +0 -0
  114. /package/web/.next/static/{XB638PEDChQhwk6wSMrSh → PpaOWDfndYJrA-tJYr7gU}/_clientMiddlewareManifest.json +0 -0
  115. /package/web/.next/static/{XB638PEDChQhwk6wSMrSh → PpaOWDfndYJrA-tJYr7gU}/_ssgManifest.js +0 -0
@@ -1,5 +1,5 @@
1
- import { C as Checkpoint, F as FileBackup, M as ModelMessage, a as Message, S as Session, L as LoadedSkill, T as TodoItem, b as ToolExecution, A as ActiveStream, I as IndexStatusRecord, c as IndexedChunk, d as SubagentExecution, e as SubagentStep, f as Terminal } from '../schema-Bq4tID-f.js';
2
- export { N as NewActiveStream, g as NewCheckpoint, h as NewFileBackup, i as NewIndexStatusRecord, j as NewIndexedChunk, k as NewMessage, l as NewSession, m as NewSubagentExecution, n as NewTerminal, o as NewTodoItem, p as NewToolExecution, q as SessionConfig, r as TaskConfig, U as UserContentPart, s as UserFilePart, t as UserImagePart, u as UserModelMessage, v as UserTextPart } from '../schema-Bq4tID-f.js';
1
+ import { C as Checkpoint, F as FileBackup, M as ModelMessage, a as Message, S as Session, L as LoadedSkill, T as TodoItem, b as ToolExecution, A as ActiveStream, I as IndexStatusRecord, c as IndexedChunk, d as SubagentExecution, e as SubagentStep, f as Terminal } from '../schema-XcP0dedO.js';
2
+ export { N as NewActiveStream, g as NewCheckpoint, h as NewFileBackup, i as NewIndexStatusRecord, j as NewIndexedChunk, k as NewMessage, l as NewSession, m as NewSubagentExecution, n as NewTerminal, o as NewTodoItem, p as NewToolExecution, q as SessionConfig, r as TaskConfig, U as UserContentPart, s as UserFilePart, t as UserImagePart, u as UserModelMessage, v as UserTextPart } from '../schema-XcP0dedO.js';
3
3
  import 'drizzle-orm/sqlite-core';
4
4
 
5
5
  /**
@@ -1,7 +1,7 @@
1
1
  import { ModelMessage, streamText } from 'ai';
2
- import { S as Session, b as ToolExecution, r as TaskConfig } from './schema-Bq4tID-f.js';
2
+ import { S as Session, b as ToolExecution, r as TaskConfig } from './schema-XcP0dedO.js';
3
3
  import { z } from 'zod';
4
- import { B as BashToolProgress, W as WriteFileProgress, S as SearchToolProgress } from './search-BRnGaIl-.js';
4
+ import { B as BashToolProgress, W as WriteFileProgress, S as SearchToolProgress } from './search-CCffrVJE.js';
5
5
 
6
6
  declare const ToolApprovalConfigSchema: z.ZodObject<{
7
7
  bash: z.ZodDefault<z.ZodOptional<z.ZodBoolean>>;
@@ -37,21 +37,21 @@ declare const SessionConfigSchema: z.ZodObject<{
37
37
  error: z.ZodOptional<z.ZodString>;
38
38
  iterations: z.ZodOptional<z.ZodNumber>;
39
39
  }, "strip", z.ZodTypeAny, {
40
- status: "completed" | "running" | "failed";
41
- enabled: boolean;
40
+ status: "completed" | "failed" | "running";
42
41
  outputSchema: Record<string, unknown>;
42
+ enabled: boolean;
43
43
  error?: string | undefined;
44
- webhookUrl?: string | undefined;
45
44
  maxIterations?: number | undefined;
45
+ webhookUrl?: string | undefined;
46
46
  result?: unknown;
47
47
  iterations?: number | undefined;
48
48
  }, {
49
- status: "completed" | "running" | "failed";
50
- enabled: boolean;
49
+ status: "completed" | "failed" | "running";
51
50
  outputSchema: Record<string, unknown>;
51
+ enabled: boolean;
52
52
  error?: string | undefined;
53
- webhookUrl?: string | undefined;
54
53
  maxIterations?: number | undefined;
54
+ webhookUrl?: string | undefined;
55
55
  result?: unknown;
56
56
  iterations?: number | undefined;
57
57
  }>>;
@@ -59,32 +59,32 @@ declare const SessionConfigSchema: z.ZodObject<{
59
59
  maxContextChars: number;
60
60
  toolApprovals?: Record<string, boolean> | undefined;
61
61
  approvalWebhook?: string | undefined;
62
- skillsDirectory?: string | undefined;
63
62
  task?: {
64
- status: "completed" | "running" | "failed";
65
- enabled: boolean;
63
+ status: "completed" | "failed" | "running";
66
64
  outputSchema: Record<string, unknown>;
65
+ enabled: boolean;
67
66
  error?: string | undefined;
68
- webhookUrl?: string | undefined;
69
67
  maxIterations?: number | undefined;
68
+ webhookUrl?: string | undefined;
70
69
  result?: unknown;
71
70
  iterations?: number | undefined;
72
71
  } | undefined;
72
+ skillsDirectory?: string | undefined;
73
73
  }, {
74
74
  toolApprovals?: Record<string, boolean> | undefined;
75
75
  approvalWebhook?: string | undefined;
76
- skillsDirectory?: string | undefined;
77
- maxContextChars?: number | undefined;
78
76
  task?: {
79
- status: "completed" | "running" | "failed";
80
- enabled: boolean;
77
+ status: "completed" | "failed" | "running";
81
78
  outputSchema: Record<string, unknown>;
79
+ enabled: boolean;
82
80
  error?: string | undefined;
83
- webhookUrl?: string | undefined;
84
81
  maxIterations?: number | undefined;
82
+ webhookUrl?: string | undefined;
85
83
  result?: unknown;
86
84
  iterations?: number | undefined;
87
85
  } | undefined;
86
+ skillsDirectory?: string | undefined;
87
+ maxContextChars?: number | undefined;
88
88
  }>;
89
89
  declare const SparkcoderConfigSchema: z.ZodObject<{
90
90
  defaultModel: z.ZodDefault<z.ZodString>;
@@ -179,6 +179,7 @@ declare const SparkcoderConfigSchema: z.ZodObject<{
179
179
  exclude?: string[] | undefined;
180
180
  }>>;
181
181
  }, "strip", z.ZodTypeAny, {
182
+ defaultModel: string;
182
183
  toolApprovals: {
183
184
  bash: boolean;
184
185
  write_file: boolean;
@@ -186,7 +187,6 @@ declare const SparkcoderConfigSchema: z.ZodObject<{
186
187
  load_skill: boolean;
187
188
  todo: boolean;
188
189
  };
189
- defaultModel: string;
190
190
  skills: {
191
191
  directory: string;
192
192
  additionalDirectories: string[];
@@ -217,6 +217,7 @@ declare const SparkcoderConfigSchema: z.ZodObject<{
217
217
  namespace?: string | undefined;
218
218
  } | undefined;
219
219
  }, {
220
+ defaultModel?: string | undefined;
220
221
  workingDirectory?: string | undefined;
221
222
  toolApprovals?: {
222
223
  bash?: boolean | undefined;
@@ -226,7 +227,6 @@ declare const SparkcoderConfigSchema: z.ZodObject<{
226
227
  todo?: boolean | undefined;
227
228
  } | undefined;
228
229
  approvalWebhook?: string | undefined;
229
- defaultModel?: string | undefined;
230
230
  skills?: {
231
231
  directory?: string | undefined;
232
232
  additionalDirectories?: string[] | undefined;
package/dist/index.d.ts CHANGED
@@ -1,11 +1,11 @@
1
- import { R as ResolvedConfig } from './index-DqaHLgSC.js';
2
- export { A as Agent, a as AgentOptions, b as AgentRunOptions, c as AgentStreamResult, S as SparkcoderConfig, T as ToolApprovalConfig } from './index-DqaHLgSC.js';
1
+ import { R as ResolvedConfig } from './index-Dm6wGcYv.js';
2
+ export { A as Agent, a as AgentOptions, b as AgentRunOptions, c as AgentStreamResult, S as SparkcoderConfig, T as ToolApprovalConfig } from './index-Dm6wGcYv.js';
3
3
  export { ServerOptions, createApp, startServer, stopServer } from './server/index.js';
4
4
  export { checkpointQueries, closeDatabase, fileBackupQueries, getDb, initDatabase, messageQueries, sessionQueries, skillQueries, todoQueries, toolExecutionQueries } from './db/index.js';
5
- import { F as FileBackup, C as Checkpoint } from './schema-Bq4tID-f.js';
6
- export { a as Message, M as ModelMessage, S as Session, q as SessionConfig, f as Terminal, T as TodoItem, b as ToolExecution } from './schema-Bq4tID-f.js';
5
+ import { F as FileBackup, C as Checkpoint } from './schema-XcP0dedO.js';
6
+ export { a as Message, M as ModelMessage, S as Session, q as SessionConfig, f as Terminal, T as TodoItem, b as ToolExecution } from './schema-XcP0dedO.js';
7
7
  export { createLoadSkillTool, createReadFileTool, createTodoTool, createTools } from './tools/index.js';
8
- export { c as createBashTool, a as createWriteFileTool } from './search-BRnGaIl-.js';
8
+ export { c as createBashTool, a as createWriteFileTool } from './search-CCffrVJE.js';
9
9
  import 'ai';
10
10
  import 'zod';
11
11
  import 'hono/types';
package/dist/index.js CHANGED
@@ -1858,10 +1858,6 @@ var init_stream_proxy = __esm({
1858
1858
  console.log(`[BROWSER-WS] Connected to ${url} (after ${this.reconnectAttempts} retries)`);
1859
1859
  this.reconnectAttempts = 0;
1860
1860
  this._connected = true;
1861
- this.emit("status", {
1862
- connected: true,
1863
- screencasting: true
1864
- });
1865
1861
  });
1866
1862
  this.ws.on("message", (raw) => {
1867
1863
  try {
@@ -1947,6 +1943,17 @@ var init_stream_proxy = __esm({
1947
1943
  this.ws.send(JSON.stringify(event));
1948
1944
  }
1949
1945
  }
1946
+ /**
1947
+ * Ask the StreamServer to send its current status (triggers sendStatus and
1948
+ * re-evaluates screencasting). Useful when listeners are replaced on a new
1949
+ * stream and we want a fresh status event.
1950
+ */
1951
+ requestStatus() {
1952
+ if (this.ws?.readyState === WebSocket.OPEN) {
1953
+ console.log(`[BROWSER-WS] Requesting fresh status from StreamServer`);
1954
+ this.ws.send(JSON.stringify({ type: "status" }));
1955
+ }
1956
+ }
1950
1957
  destroy() {
1951
1958
  console.log(`[BROWSER-WS] Destroying proxy for port ${this.port} (emitted ${this.frameCount} frames, throttled ${this.throttledCount})`);
1952
1959
  this.destroyed = true;
@@ -8507,37 +8514,40 @@ ${prompt}` });
8507
8514
  recorder.start();
8508
8515
  sessionRecorders.set(sessionId, recorder);
8509
8516
  }
8510
- if (proxy.listenerCount("frame") === 0) {
8511
- console.log(`[BROWSER-STREAM:${streamId}] Attaching frame+status listeners to proxy`);
8512
- proxy.on("frame", (frame) => {
8513
- sseBrowserFrameCount++;
8514
- if (sseBrowserFrameCount === 1) {
8515
- console.log(`[BROWSER-STREAM:${streamId}] First browser frame received! dataSize=${frame.data?.length ?? 0} writerClosed=${writerClosed}`);
8516
- } else if (sseBrowserFrameCount % 50 === 0) {
8517
- console.log(`[BROWSER-STREAM:${streamId}] Browser frame #${sseBrowserFrameCount} (writerClosed=${writerClosed})`);
8518
- }
8519
- const rec = sessionRecorders.get(sessionId);
8520
- rec?.addFrame(frame);
8521
- writeSSE(JSON.stringify({
8522
- type: "browser-frame",
8523
- data: frame.data,
8524
- metadata: frame.metadata
8525
- })).catch((err) => {
8526
- console.warn(`[BROWSER-STREAM:${streamId}] Failed to send browser-frame via SSE:`, err);
8527
- });
8517
+ const oldFrameListeners = proxy.listenerCount("frame");
8518
+ if (oldFrameListeners > 0) {
8519
+ console.log(`[BROWSER-STREAM:${streamId}] Replacing ${oldFrameListeners} stale frame listener(s) from previous stream`);
8520
+ proxy.removeAllListeners("frame");
8521
+ proxy.removeAllListeners("status");
8522
+ }
8523
+ console.log(`[BROWSER-STREAM:${streamId}] Attaching frame+status listeners to proxy`);
8524
+ proxy.on("frame", (frame) => {
8525
+ sseBrowserFrameCount++;
8526
+ if (sseBrowserFrameCount === 1) {
8527
+ console.log(`[BROWSER-STREAM:${streamId}] First browser frame received! dataSize=${frame.data?.length ?? 0} writerClosed=${writerClosed}`);
8528
+ } else if (sseBrowserFrameCount % 50 === 0) {
8529
+ console.log(`[BROWSER-STREAM:${streamId}] Browser frame #${sseBrowserFrameCount} (writerClosed=${writerClosed})`);
8530
+ }
8531
+ const rec = sessionRecorders.get(sessionId);
8532
+ rec?.addFrame(frame);
8533
+ writeSSE(JSON.stringify({
8534
+ type: "browser-frame",
8535
+ data: frame.data,
8536
+ metadata: frame.metadata
8537
+ })).catch((err) => {
8538
+ console.warn(`[BROWSER-STREAM:${streamId}] Failed to send browser-frame via SSE:`, err);
8528
8539
  });
8529
- proxy.on("status", (s) => {
8530
- console.log(`[BROWSER-STREAM:${streamId}] Browser status event: connected=${s.connected} screencasting=${s.screencasting} viewport=${s.viewportWidth}x${s.viewportHeight}`);
8531
- writeSSE(JSON.stringify({
8532
- type: "browser-status",
8533
- ...s
8534
- })).catch((err) => {
8535
- console.warn(`[BROWSER-STREAM:${streamId}] Failed to send browser-status via SSE:`, err);
8536
- });
8540
+ });
8541
+ proxy.on("status", (s) => {
8542
+ console.log(`[BROWSER-STREAM:${streamId}] Browser status event: connected=${s.connected} screencasting=${s.screencasting} viewport=${s.viewportWidth}x${s.viewportHeight}`);
8543
+ writeSSE(JSON.stringify({
8544
+ type: "browser-status",
8545
+ ...s
8546
+ })).catch((err) => {
8547
+ console.warn(`[BROWSER-STREAM:${streamId}] Failed to send browser-status via SSE:`, err);
8537
8548
  });
8538
- } else {
8539
- console.log(`[BROWSER-STREAM:${streamId}] Frame listeners already attached (count=${proxy.listenerCount("frame")}), skipping`);
8540
- }
8549
+ });
8550
+ proxy.requestStatus();
8541
8551
  }
8542
8552
  },
8543
8553
  onStepFinish: async () => {
@@ -9032,37 +9042,40 @@ agents.post(
9032
9042
  recorder.start();
9033
9043
  sessionRecorders.set(session.id, recorder);
9034
9044
  }
9035
- if (proxy.listenerCount("frame") === 0) {
9036
- console.log(`[BROWSER-STREAM:${streamId}] Attaching frame+status listeners to proxy`);
9037
- proxy.on("frame", (frame) => {
9038
- sseBrowserFrameCount++;
9039
- if (sseBrowserFrameCount === 1) {
9040
- console.log(`[BROWSER-STREAM:${streamId}] First browser frame received! dataSize=${frame.data?.length ?? 0} writerClosed=${writerClosed}`);
9041
- } else if (sseBrowserFrameCount % 50 === 0) {
9042
- console.log(`[BROWSER-STREAM:${streamId}] Browser frame #${sseBrowserFrameCount} (writerClosed=${writerClosed})`);
9043
- }
9044
- const rec = sessionRecorders.get(session.id);
9045
- rec?.addFrame(frame);
9046
- writeSSE(JSON.stringify({
9047
- type: "browser-frame",
9048
- data: frame.data,
9049
- metadata: frame.metadata
9050
- })).catch((err) => {
9051
- console.warn(`[BROWSER-STREAM:${streamId}] Failed to send browser-frame via SSE:`, err);
9052
- });
9045
+ const oldFrameListeners = proxy.listenerCount("frame");
9046
+ if (oldFrameListeners > 0) {
9047
+ console.log(`[BROWSER-STREAM:${streamId}] Replacing ${oldFrameListeners} stale frame listener(s) from previous stream`);
9048
+ proxy.removeAllListeners("frame");
9049
+ proxy.removeAllListeners("status");
9050
+ }
9051
+ console.log(`[BROWSER-STREAM:${streamId}] Attaching frame+status listeners to proxy`);
9052
+ proxy.on("frame", (frame) => {
9053
+ sseBrowserFrameCount++;
9054
+ if (sseBrowserFrameCount === 1) {
9055
+ console.log(`[BROWSER-STREAM:${streamId}] First browser frame received! dataSize=${frame.data?.length ?? 0} writerClosed=${writerClosed}`);
9056
+ } else if (sseBrowserFrameCount % 50 === 0) {
9057
+ console.log(`[BROWSER-STREAM:${streamId}] Browser frame #${sseBrowserFrameCount} (writerClosed=${writerClosed})`);
9058
+ }
9059
+ const rec = sessionRecorders.get(session.id);
9060
+ rec?.addFrame(frame);
9061
+ writeSSE(JSON.stringify({
9062
+ type: "browser-frame",
9063
+ data: frame.data,
9064
+ metadata: frame.metadata
9065
+ })).catch((err) => {
9066
+ console.warn(`[BROWSER-STREAM:${streamId}] Failed to send browser-frame via SSE:`, err);
9053
9067
  });
9054
- proxy.on("status", (s) => {
9055
- console.log(`[BROWSER-STREAM:${streamId}] Browser status event: connected=${s.connected} screencasting=${s.screencasting} viewport=${s.viewportWidth}x${s.viewportHeight}`);
9056
- writeSSE(JSON.stringify({
9057
- type: "browser-status",
9058
- ...s
9059
- })).catch((err) => {
9060
- console.warn(`[BROWSER-STREAM:${streamId}] Failed to send browser-status via SSE:`, err);
9061
- });
9068
+ });
9069
+ proxy.on("status", (s) => {
9070
+ console.log(`[BROWSER-STREAM:${streamId}] Browser status event: connected=${s.connected} screencasting=${s.screencasting} viewport=${s.viewportWidth}x${s.viewportHeight}`);
9071
+ writeSSE(JSON.stringify({
9072
+ type: "browser-status",
9073
+ ...s
9074
+ })).catch((err) => {
9075
+ console.warn(`[BROWSER-STREAM:${streamId}] Failed to send browser-status via SSE:`, err);
9062
9076
  });
9063
- } else {
9064
- console.log(`[BROWSER-STREAM:${streamId}] Frame listeners already attached (count=${proxy.listenerCount("frame")}), skipping`);
9065
- }
9077
+ });
9078
+ proxy.requestStatus();
9066
9079
  }
9067
9080
  },
9068
9081
  onStepFinish: async () => {