@electric-agent/studio 1.7.0 → 1.12.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (119) hide show
  1. package/dist/active-sessions.d.ts +2 -0
  2. package/dist/active-sessions.d.ts.map +1 -1
  3. package/dist/active-sessions.js +4 -0
  4. package/dist/active-sessions.js.map +1 -1
  5. package/dist/api-schemas.d.ts +225 -0
  6. package/dist/api-schemas.d.ts.map +1 -0
  7. package/dist/api-schemas.js +95 -0
  8. package/dist/api-schemas.js.map +1 -0
  9. package/dist/bridge/claude-code-base.d.ts +121 -0
  10. package/dist/bridge/claude-code-base.d.ts.map +1 -0
  11. package/dist/bridge/claude-code-base.js +263 -0
  12. package/dist/bridge/claude-code-base.js.map +1 -0
  13. package/dist/bridge/claude-code-docker.d.ts +13 -73
  14. package/dist/bridge/claude-code-docker.d.ts.map +1 -1
  15. package/dist/bridge/claude-code-docker.js +91 -302
  16. package/dist/bridge/claude-code-docker.js.map +1 -1
  17. package/dist/bridge/claude-code-sprites.d.ts +12 -59
  18. package/dist/bridge/claude-code-sprites.d.ts.map +1 -1
  19. package/dist/bridge/claude-code-sprites.js +88 -281
  20. package/dist/bridge/claude-code-sprites.js.map +1 -1
  21. package/dist/bridge/claude-md-generator.d.ts +15 -3
  22. package/dist/bridge/claude-md-generator.d.ts.map +1 -1
  23. package/dist/bridge/claude-md-generator.js +79 -98
  24. package/dist/bridge/claude-md-generator.js.map +1 -1
  25. package/dist/bridge/codex-docker.d.ts +56 -51
  26. package/dist/bridge/codex-docker.js +222 -230
  27. package/dist/bridge/codex-json-parser.d.ts +11 -11
  28. package/dist/bridge/codex-json-parser.js +231 -238
  29. package/dist/bridge/codex-md-generator.d.ts +3 -3
  30. package/dist/bridge/codex-md-generator.js +42 -32
  31. package/dist/bridge/codex-sprites.d.ts +50 -45
  32. package/dist/bridge/codex-sprites.js +212 -222
  33. package/dist/bridge/daytona.d.ts +25 -25
  34. package/dist/bridge/daytona.js +131 -136
  35. package/dist/bridge/docker-stdio.d.ts +21 -21
  36. package/dist/bridge/docker-stdio.js +126 -132
  37. package/dist/bridge/hosted.d.ts +3 -2
  38. package/dist/bridge/hosted.d.ts.map +1 -1
  39. package/dist/bridge/hosted.js +4 -0
  40. package/dist/bridge/hosted.js.map +1 -1
  41. package/dist/bridge/message-parser.d.ts +24 -0
  42. package/dist/bridge/message-parser.d.ts.map +1 -0
  43. package/dist/bridge/message-parser.js +39 -0
  44. package/dist/bridge/message-parser.js.map +1 -0
  45. package/dist/bridge/role-skills.d.ts +25 -0
  46. package/dist/bridge/role-skills.d.ts.map +1 -0
  47. package/dist/bridge/role-skills.js +120 -0
  48. package/dist/bridge/role-skills.js.map +1 -0
  49. package/dist/bridge/room-messaging-skill.d.ts +11 -0
  50. package/dist/bridge/room-messaging-skill.d.ts.map +1 -0
  51. package/dist/bridge/room-messaging-skill.js +41 -0
  52. package/dist/bridge/room-messaging-skill.js.map +1 -0
  53. package/dist/bridge/sprites.d.ts +22 -22
  54. package/dist/bridge/sprites.js +123 -128
  55. package/dist/bridge/types.d.ts +4 -10
  56. package/dist/bridge/types.d.ts.map +1 -1
  57. package/dist/client/assets/index-BfvQSMwH.css +1 -0
  58. package/dist/client/assets/index-CtOOaA2Q.js +235 -0
  59. package/dist/client/index.html +2 -2
  60. package/dist/github-app.d.ts +14 -0
  61. package/dist/github-app.d.ts.map +1 -0
  62. package/dist/github-app.js +62 -0
  63. package/dist/github-app.js.map +1 -0
  64. package/dist/github-app.test.d.ts +2 -0
  65. package/dist/github-app.test.d.ts.map +1 -0
  66. package/dist/github-app.test.js +62 -0
  67. package/dist/github-app.test.js.map +1 -0
  68. package/dist/index.d.ts +4 -3
  69. package/dist/index.d.ts.map +1 -1
  70. package/dist/index.js +3 -3
  71. package/dist/index.js.map +1 -1
  72. package/dist/invite-code.d.ts +5 -0
  73. package/dist/invite-code.d.ts.map +1 -0
  74. package/dist/invite-code.js +14 -0
  75. package/dist/invite-code.js.map +1 -0
  76. package/dist/project-utils.d.ts.map +1 -1
  77. package/dist/project-utils.js.map +1 -1
  78. package/dist/registry.d.ts +11 -4
  79. package/dist/registry.d.ts.map +1 -1
  80. package/dist/registry.js +1 -1
  81. package/dist/registry.js.map +1 -1
  82. package/dist/room-router.d.ts +73 -0
  83. package/dist/room-router.d.ts.map +1 -0
  84. package/dist/room-router.js +345 -0
  85. package/dist/room-router.js.map +1 -0
  86. package/dist/sandbox/docker.d.ts +1 -0
  87. package/dist/sandbox/docker.d.ts.map +1 -1
  88. package/dist/sandbox/docker.js +56 -6
  89. package/dist/sandbox/docker.js.map +1 -1
  90. package/dist/sandbox/index.d.ts +0 -1
  91. package/dist/sandbox/index.d.ts.map +1 -1
  92. package/dist/sandbox/index.js +0 -1
  93. package/dist/sandbox/index.js.map +1 -1
  94. package/dist/sandbox/sprites.d.ts +1 -0
  95. package/dist/sandbox/sprites.d.ts.map +1 -1
  96. package/dist/sandbox/sprites.js +91 -10
  97. package/dist/sandbox/sprites.js.map +1 -1
  98. package/dist/sandbox/types.d.ts +9 -2
  99. package/dist/sandbox/types.d.ts.map +1 -1
  100. package/dist/server.d.ts +12 -0
  101. package/dist/server.d.ts.map +1 -1
  102. package/dist/server.js +906 -445
  103. package/dist/server.js.map +1 -1
  104. package/dist/session-auth.d.ts +9 -0
  105. package/dist/session-auth.d.ts.map +1 -1
  106. package/dist/session-auth.js +30 -0
  107. package/dist/session-auth.js.map +1 -1
  108. package/dist/sessions.d.ts +1 -1
  109. package/dist/streams.d.ts +2 -6
  110. package/dist/streams.d.ts.map +1 -1
  111. package/dist/streams.js +6 -17
  112. package/dist/streams.js.map +1 -1
  113. package/dist/validate.d.ts +10 -0
  114. package/dist/validate.d.ts.map +1 -0
  115. package/dist/validate.js +24 -0
  116. package/dist/validate.js.map +1 -0
  117. package/package.json +6 -9
  118. package/dist/client/assets/index-D5-jqAV-.js +0 -234
  119. package/dist/client/assets/index-YyyiO26y.css +0 -1
@@ -8,52 +8,57 @@
8
8
  * Codex runs in one-shot mode (`codex exec --json`) and exits after completing.
9
9
  * On iterate (follow-up message), the bridge respawns Codex with the new prompt.
10
10
  */
11
- import type { EngineEvent } from "@electric-agent/protocol";
12
- import type { Sprite } from "@fly/sprites";
13
- import type { StreamConnectionInfo } from "../streams.js";
14
- import type { SessionBridge } from "./types.js";
11
+ import type { EngineEvent } from "@electric-agent/protocol"
12
+ import type { Sprite } from "@fly/sprites"
13
+ import type { StreamConnectionInfo } from "../streams.js"
14
+ import type { SessionBridge } from "./types.js"
15
15
  export interface CodexSpritesConfig {
16
- /** Initial prompt (the user's app description or task) */
17
- prompt: string;
18
- /** Working directory inside the sprite */
19
- cwd: string;
20
- /** Model to use (default: o4-mini) */
21
- model?: string;
22
- /** Additional CLI flags */
23
- extraFlags?: string[];
16
+ /** Initial prompt (the user's app description or task) */
17
+ prompt: string
18
+ /** Working directory inside the sprite */
19
+ cwd: string
20
+ /** Model to use (default: o4-mini) */
21
+ model?: string
22
+ /** Additional CLI flags */
23
+ extraFlags?: string[]
24
24
  }
25
25
  export declare class CodexSpritesBridge implements SessionBridge {
26
- readonly sessionId: string;
27
- readonly streamUrl: string;
28
- readonly streamHeaders: Record<string, string>;
29
- private sprite;
30
- private config;
31
- private writer;
32
- private parser;
33
- private agentEventCallbacks;
34
- private completeCallbacks;
35
- private closed;
36
- private cmd;
37
- /** Codex thread ID captured from thread.started — used for resume */
38
- private codexThreadId;
39
- /** Whether a Codex process is currently running */
40
- private running;
41
- /** Whether the parser already emitted a session_end */
42
- private resultReceived;
43
- constructor(sessionId: string, connection: StreamConnectionInfo, sprite: Sprite, config: CodexSpritesConfig);
44
- emit(event: EngineEvent): Promise<void>;
45
- sendCommand(cmd: Record<string, unknown>): Promise<void>;
46
- sendGateResponse(_gate: string, _value: Record<string, unknown>): Promise<void>;
47
- onAgentEvent(cb: (event: EngineEvent) => void): void;
48
- onComplete(cb: (success: boolean) => void): void;
49
- start(): Promise<void>;
50
- close(): void;
51
- /**
52
- * Spawn a new Codex process. Called for both the initial prompt
53
- * and follow-up iterate messages.
54
- */
55
- private spawnCodex;
56
- private handleLine;
57
- private dispatchEvent;
26
+ readonly sessionId: string
27
+ readonly streamUrl: string
28
+ readonly streamHeaders: Record<string, string>
29
+ private sprite
30
+ private config
31
+ private writer
32
+ private parser
33
+ private agentEventCallbacks
34
+ private completeCallbacks
35
+ private closed
36
+ private cmd
37
+ /** Codex thread ID captured from thread.started — used for resume */
38
+ private codexThreadId
39
+ /** Whether a Codex process is currently running */
40
+ private running
41
+ /** Whether the parser already emitted a session_end */
42
+ private resultReceived
43
+ constructor(
44
+ sessionId: string,
45
+ connection: StreamConnectionInfo,
46
+ sprite: Sprite,
47
+ config: CodexSpritesConfig,
48
+ )
49
+ emit(event: EngineEvent): Promise<void>
50
+ sendCommand(cmd: Record<string, unknown>): Promise<void>
51
+ sendGateResponse(_gate: string, _value: Record<string, unknown>): Promise<void>
52
+ onAgentEvent(cb: (event: EngineEvent) => void): void
53
+ onComplete(cb: (success: boolean) => void): void
54
+ start(): Promise<void>
55
+ close(): void
56
+ /**
57
+ * Spawn a new Codex process. Called for both the initial prompt
58
+ * and follow-up iterate messages.
59
+ */
60
+ private spawnCodex
61
+ private handleLine
62
+ private dispatchEvent
58
63
  }
59
- //# sourceMappingURL=codex-sprites.d.ts.map
64
+ //# sourceMappingURL=codex-sprites.d.ts.map
@@ -8,230 +8,220 @@
8
8
  * Codex runs in one-shot mode (`codex exec --json`) and exits after completing.
9
9
  * On iterate (follow-up message), the bridge respawns Codex with the new prompt.
10
10
  */
11
- import * as readline from "node:readline";
12
- import { DurableStream } from "@durable-streams/client";
13
- import { ts } from "@electric-agent/protocol";
14
- import { SpriteCommand } from "@fly/sprites";
15
- import { createCodexJsonParser } from "./codex-json-parser.js";
11
+ import * as readline from "node:readline"
12
+ import { DurableStream } from "@durable-streams/client"
13
+ import { ts } from "@electric-agent/protocol"
14
+ import { SpriteCommand } from "@fly/sprites"
15
+ import { createCodexJsonParser } from "./codex-json-parser.js"
16
16
  export class CodexSpritesBridge {
17
- sessionId;
18
- streamUrl;
19
- streamHeaders;
20
- sprite;
21
- config;
22
- writer;
23
- parser = createCodexJsonParser();
24
- agentEventCallbacks = [];
25
- completeCallbacks = [];
26
- closed = false;
27
- cmd = null;
28
- /** Codex thread ID captured from thread.started — used for resume */
29
- codexThreadId = null;
30
- /** Whether a Codex process is currently running */
31
- running = false;
32
- /** Whether the parser already emitted a session_end */
33
- resultReceived = false;
34
- constructor(sessionId, connection, sprite, config) {
35
- this.sessionId = sessionId;
36
- this.streamUrl = connection.url;
37
- this.streamHeaders = connection.headers;
38
- this.sprite = sprite;
39
- this.config = config;
40
- this.writer = new DurableStream({
41
- url: connection.url,
42
- headers: connection.headers,
43
- contentType: "application/json",
44
- });
45
- }
46
- async emit(event) {
47
- if (this.closed)
48
- return;
49
- const msg = { source: "server", ...event };
50
- await this.writer.append(JSON.stringify(msg));
51
- }
52
- async sendCommand(cmd) {
53
- if (this.closed)
54
- return;
55
- if (cmd.command === "iterate" && typeof cmd.request === "string") {
56
- this.spawnCodex(cmd.request);
57
- return;
58
- }
59
- console.log(`[codex-sprites] Ignoring unsupported command: ${cmd.command}`);
60
- }
61
- async sendGateResponse(_gate, _value) {
62
- // Codex exec --json doesn't support stdin user messages mid-run
63
- }
64
- onAgentEvent(cb) {
65
- this.agentEventCallbacks.push(cb);
66
- }
67
- onComplete(cb) {
68
- this.completeCallbacks.push(cb);
69
- }
70
- async start() {
71
- if (this.closed)
72
- return;
73
- this.spawnCodex(this.config.prompt);
74
- }
75
- close() {
76
- this.closed = true;
77
- if (this.cmd) {
78
- try {
79
- this.cmd.kill();
80
- }
81
- catch {
82
- // Process may already be dead
83
- }
84
- this.cmd = null;
85
- }
86
- }
87
- // -----------------------------------------------------------------------
88
- // Private helpers
89
- // -----------------------------------------------------------------------
90
- /**
91
- * Spawn a new Codex process. Called for both the initial prompt
92
- * and follow-up iterate messages.
93
- */
94
- spawnCodex(prompt) {
95
- // Kill any existing process
96
- if (this.cmd) {
97
- try {
98
- this.cmd.kill();
99
- }
100
- catch {
101
- // Already dead
102
- }
103
- this.cmd = null;
104
- }
105
- // Reset parser state for the new process
106
- this.parser = createCodexJsonParser();
107
- this.resultReceived = false;
108
- this.running = true;
109
- const model = this.config.model ?? "o4-mini";
110
- // Build the codex CLI command
111
- const codexArgs = [
112
- "exec",
113
- "--json",
114
- "--full-auto",
115
- "--model",
116
- model,
117
- ...(this.config.extraFlags ?? []),
118
- "-q",
119
- prompt,
120
- ];
121
- // Escape for bash — use bash -c with properly escaped args
122
- const escapedArgs = codexArgs.map((a) => `'${a.replace(/'/g, "'\\''")}'`).join(" ");
123
- const fullCmd = `source /etc/profile.d/npm-global.sh 2>/dev/null; source /etc/profile.d/electric-agent.sh 2>/dev/null; cd '${this.config.cwd}' && codex ${escapedArgs}`;
124
- // Use SpriteCommand with tty:true (for streaming)
125
- this.cmd = new SpriteCommand(this.sprite, "bash", ["-c", fullCmd], {
126
- tty: true,
127
- });
128
- this.cmd.start();
129
- console.log(`[codex-sprites] Started: session=${this.sessionId}`);
130
- const currentCmd = this.cmd;
131
- // Read stdout line by line (exec --json NDJSON)
132
- const rl = readline.createInterface({
133
- input: currentCmd.stdout,
134
- terminal: false,
135
- });
136
- rl.on("line", (line) => {
137
- if (this.closed)
138
- return;
139
- this.handleLine(line);
140
- });
141
- // Log stderr
142
- const stderrRl = readline.createInterface({
143
- input: currentCmd.stderr,
144
- terminal: false,
145
- });
146
- stderrRl.on("line", (line) => {
147
- if (!this.closed) {
148
- console.error(`[codex-sprites:stderr] ${line}`);
149
- }
150
- });
151
- // Handle process exit — defer to let pending readline events flush first
152
- currentCmd.on("exit", (code) => {
153
- console.log(`[codex-sprites] Process exited: code=${code} session=${this.sessionId}`);
154
- setTimeout(() => {
155
- // Capture thread ID from parser state before marking not running
156
- if (this.parser.state.threadId) {
157
- this.codexThreadId = this.parser.state.threadId;
158
- }
159
- this.running = false;
160
- // Emit session_end if the parser didn't already
161
- if (!this.closed && !this.resultReceived) {
162
- const endEvent = {
163
- type: "session_end",
164
- success: code === 0,
165
- ts: ts(),
166
- };
167
- this.dispatchEvent(endEvent);
168
- }
169
- }, 100);
170
- });
171
- }
172
- handleLine(line) {
173
- // Strip ANSI escape sequences and terminal control chars added by tty mode
174
- const cleaned = stripAnsi(line).trim();
175
- if (!cleaned)
176
- return;
177
- const events = this.parser.parse(cleaned);
178
- for (const event of events) {
179
- this.dispatchEvent(event);
180
- }
181
- }
182
- dispatchEvent(event) {
183
- const msg = { source: "agent", ...event };
184
- this.writer.append(JSON.stringify(msg)).catch(() => { });
185
- // Track session_end to prevent duplicates
186
- if (event.type === "session_end") {
187
- this.resultReceived = true;
188
- }
189
- // Detect dev:start in Bash tool_use → emit app_ready for the UI preview
190
- if (event.type === "pre_tool_use" && event.tool_name === "Bash") {
191
- const cmd = event.tool_input?.command;
192
- if (typeof cmd === "string" && /\bdev:start\b/.test(cmd)) {
193
- const appReady = { type: "app_ready", ts: ts() };
194
- const appReadyMsg = { source: "agent", ...appReady };
195
- this.writer.append(JSON.stringify(appReadyMsg)).catch(() => { });
196
- for (const cb of this.agentEventCallbacks) {
197
- try {
198
- cb(appReady);
199
- }
200
- catch {
201
- // Swallow
202
- }
203
- }
204
- }
205
- }
206
- for (const cb of this.agentEventCallbacks) {
207
- try {
208
- cb(event);
209
- }
210
- catch {
211
- // Swallow callback errors
212
- }
213
- }
214
- if (event.type === "session_end" && "success" in event) {
215
- const success = event.success;
216
- for (const cb of this.completeCallbacks) {
217
- try {
218
- cb(success);
219
- }
220
- catch {
221
- // Swallow callback errors
222
- }
223
- }
224
- }
225
- }
17
+ sessionId
18
+ streamUrl
19
+ streamHeaders
20
+ sprite
21
+ config
22
+ writer
23
+ parser = createCodexJsonParser()
24
+ agentEventCallbacks = []
25
+ completeCallbacks = []
26
+ closed = false
27
+ cmd = null
28
+ /** Codex thread ID captured from thread.started — used for resume */
29
+ codexThreadId = null
30
+ /** Whether a Codex process is currently running */
31
+ running = false
32
+ /** Whether the parser already emitted a session_end */
33
+ resultReceived = false
34
+ constructor(sessionId, connection, sprite, config) {
35
+ this.sessionId = sessionId
36
+ this.streamUrl = connection.url
37
+ this.streamHeaders = connection.headers
38
+ this.sprite = sprite
39
+ this.config = config
40
+ this.writer = new DurableStream({
41
+ url: connection.url,
42
+ headers: connection.headers,
43
+ contentType: "application/json",
44
+ })
45
+ }
46
+ async emit(event) {
47
+ if (this.closed) return
48
+ const msg = { source: "server", ...event }
49
+ await this.writer.append(JSON.stringify(msg))
50
+ }
51
+ async sendCommand(cmd) {
52
+ if (this.closed) return
53
+ if (cmd.command === "iterate" && typeof cmd.request === "string") {
54
+ this.spawnCodex(cmd.request)
55
+ return
56
+ }
57
+ console.log(`[codex-sprites] Ignoring unsupported command: ${cmd.command}`)
58
+ }
59
+ async sendGateResponse(_gate, _value) {
60
+ // Codex exec --json doesn't support stdin user messages mid-run
61
+ }
62
+ onAgentEvent(cb) {
63
+ this.agentEventCallbacks.push(cb)
64
+ }
65
+ onComplete(cb) {
66
+ this.completeCallbacks.push(cb)
67
+ }
68
+ async start() {
69
+ if (this.closed) return
70
+ this.spawnCodex(this.config.prompt)
71
+ }
72
+ close() {
73
+ this.closed = true
74
+ if (this.cmd) {
75
+ try {
76
+ this.cmd.kill()
77
+ } catch {
78
+ // Process may already be dead
79
+ }
80
+ this.cmd = null
81
+ }
82
+ }
83
+ // -----------------------------------------------------------------------
84
+ // Private helpers
85
+ // -----------------------------------------------------------------------
86
+ /**
87
+ * Spawn a new Codex process. Called for both the initial prompt
88
+ * and follow-up iterate messages.
89
+ */
90
+ spawnCodex(prompt) {
91
+ // Kill any existing process
92
+ if (this.cmd) {
93
+ try {
94
+ this.cmd.kill()
95
+ } catch {
96
+ // Already dead
97
+ }
98
+ this.cmd = null
99
+ }
100
+ // Reset parser state for the new process
101
+ this.parser = createCodexJsonParser()
102
+ this.resultReceived = false
103
+ this.running = true
104
+ const model = this.config.model ?? "o4-mini"
105
+ // Build the codex CLI command
106
+ const codexArgs = [
107
+ "exec",
108
+ "--json",
109
+ "--full-auto",
110
+ "--model",
111
+ model,
112
+ ...(this.config.extraFlags ?? []),
113
+ "-q",
114
+ prompt,
115
+ ]
116
+ // Escape for bash — use bash -c with properly escaped args
117
+ const escapedArgs = codexArgs.map((a) => `'${a.replace(/'/g, "'\\''")}'`).join(" ")
118
+ const fullCmd = `source /etc/profile.d/npm-global.sh 2>/dev/null; source /etc/profile.d/electric-agent.sh 2>/dev/null; cd '${this.config.cwd}' && codex ${escapedArgs}`
119
+ // Use SpriteCommand with tty:true (for streaming)
120
+ this.cmd = new SpriteCommand(this.sprite, "bash", ["-c", fullCmd], {
121
+ tty: true,
122
+ })
123
+ this.cmd.start()
124
+ console.log(`[codex-sprites] Started: session=${this.sessionId}`)
125
+ const currentCmd = this.cmd
126
+ // Read stdout line by line (exec --json NDJSON)
127
+ const rl = readline.createInterface({
128
+ input: currentCmd.stdout,
129
+ terminal: false,
130
+ })
131
+ rl.on("line", (line) => {
132
+ if (this.closed) return
133
+ this.handleLine(line)
134
+ })
135
+ // Log stderr
136
+ const stderrRl = readline.createInterface({
137
+ input: currentCmd.stderr,
138
+ terminal: false,
139
+ })
140
+ stderrRl.on("line", (line) => {
141
+ if (!this.closed) {
142
+ console.error(`[codex-sprites:stderr] ${line}`)
143
+ }
144
+ })
145
+ // Handle process exit — defer to let pending readline events flush first
146
+ currentCmd.on("exit", (code) => {
147
+ console.log(`[codex-sprites] Process exited: code=${code} session=${this.sessionId}`)
148
+ setTimeout(() => {
149
+ // Capture thread ID from parser state before marking not running
150
+ if (this.parser.state.threadId) {
151
+ this.codexThreadId = this.parser.state.threadId
152
+ }
153
+ this.running = false
154
+ // Emit session_end if the parser didn't already
155
+ if (!this.closed && !this.resultReceived) {
156
+ const endEvent = {
157
+ type: "session_end",
158
+ success: code === 0,
159
+ ts: ts(),
160
+ }
161
+ this.dispatchEvent(endEvent)
162
+ }
163
+ }, 100)
164
+ })
165
+ }
166
+ handleLine(line) {
167
+ // Strip ANSI escape sequences and terminal control chars added by tty mode
168
+ const cleaned = stripAnsi(line).trim()
169
+ if (!cleaned) return
170
+ const events = this.parser.parse(cleaned)
171
+ for (const event of events) {
172
+ this.dispatchEvent(event)
173
+ }
174
+ }
175
+ dispatchEvent(event) {
176
+ const msg = { source: "agent", ...event }
177
+ this.writer.append(JSON.stringify(msg)).catch(() => {})
178
+ // Track session_end to prevent duplicates
179
+ if (event.type === "session_end") {
180
+ this.resultReceived = true
181
+ }
182
+ // Detect dev:start in Bash tool_use → emit app_ready for the UI preview
183
+ if (event.type === "pre_tool_use" && event.tool_name === "Bash") {
184
+ const cmd = event.tool_input?.command
185
+ if (typeof cmd === "string" && /\bdev:start\b/.test(cmd)) {
186
+ const appReady = { type: "app_ready", ts: ts() }
187
+ const appReadyMsg = { source: "agent", ...appReady }
188
+ this.writer.append(JSON.stringify(appReadyMsg)).catch(() => {})
189
+ for (const cb of this.agentEventCallbacks) {
190
+ try {
191
+ cb(appReady)
192
+ } catch {
193
+ // Swallow
194
+ }
195
+ }
196
+ }
197
+ }
198
+ for (const cb of this.agentEventCallbacks) {
199
+ try {
200
+ cb(event)
201
+ } catch {
202
+ // Swallow callback errors
203
+ }
204
+ }
205
+ if (event.type === "session_end" && "success" in event) {
206
+ const success = event.success
207
+ for (const cb of this.completeCallbacks) {
208
+ try {
209
+ cb(success)
210
+ } catch {
211
+ // Swallow callback errors
212
+ }
213
+ }
214
+ }
215
+ }
226
216
  }
227
217
  /** Strip ANSI escape sequences and control characters from tty output */
228
218
  function stripAnsi(str) {
229
- const ESC = "\x1b";
230
- const csi = new RegExp(`${ESC}\\[[0-9;]*[a-zA-Z]`, "g");
231
- const osc1 = new RegExp(`${ESC}\\][^\\x07]*\\x07`, "g");
232
- const osc2 = new RegExp(`${ESC}\\][^${ESC}]*${ESC}\\\\`, "g");
233
- // biome-ignore lint/suspicious/noControlCharactersInRegex: strip C0 control chars except \n \r
234
- const ctrl = /[\x00-\x09\x0b\x0c\x0e-\x1f\x7f]/g;
235
- return str.replace(csi, "").replace(osc1, "").replace(osc2, "").replace(ctrl, "");
219
+ const ESC = "\x1b"
220
+ const csi = new RegExp(`${ESC}\\[[0-9;]*[a-zA-Z]`, "g")
221
+ const osc1 = new RegExp(`${ESC}\\][^\\x07]*\\x07`, "g")
222
+ const osc2 = new RegExp(`${ESC}\\][^${ESC}]*${ESC}\\\\`, "g")
223
+ // biome-ignore lint/suspicious/noControlCharactersInRegex: strip C0 control chars except \n \r
224
+ const ctrl = /[\x00-\x09\x0b\x0c\x0e-\x1f\x7f]/g
225
+ return str.replace(csi, "").replace(osc1, "").replace(osc2, "").replace(ctrl, "")
236
226
  }
237
- //# sourceMappingURL=codex-sprites.js.map
227
+ //# sourceMappingURL=codex-sprites.js.map
@@ -6,30 +6,30 @@
6
6
  * the Daytona session (for agent communication). The agent inside the
7
7
  * sandbox uses the stdio adapter — no outbound internet required.
8
8
  */
9
- import type { Sandbox } from "@daytonaio/sdk";
10
- import type { EngineEvent } from "@electric-agent/protocol";
11
- import type { StreamConnectionInfo } from "../streams.js";
12
- import type { SessionBridge } from "./types.js";
9
+ import type { Sandbox } from "@daytonaio/sdk"
10
+ import type { EngineEvent } from "@electric-agent/protocol"
11
+ import type { StreamConnectionInfo } from "../streams.js"
12
+ import type { SessionBridge } from "./types.js"
13
13
  export declare class DaytonaSessionBridge implements SessionBridge {
14
- readonly sessionId: string;
15
- readonly streamUrl: string;
16
- readonly streamHeaders: Record<string, string>;
17
- private sandbox;
18
- private writer;
19
- private agentEventCallbacks;
20
- private completeCallbacks;
21
- private closed;
22
- private cmdId;
23
- private stdoutBuffer;
24
- constructor(sessionId: string, connection: StreamConnectionInfo, sandbox: Sandbox);
25
- emit(event: EngineEvent): Promise<void>;
26
- sendCommand(cmd: Record<string, unknown>): Promise<void>;
27
- sendGateResponse(gate: string, value: Record<string, unknown>): Promise<void>;
28
- onAgentEvent(cb: (event: EngineEvent) => void): void;
29
- onComplete(cb: (success: boolean) => void): void;
30
- start(): Promise<void>;
31
- private pollLogs;
32
- private handleStdout;
33
- close(): void;
14
+ readonly sessionId: string
15
+ readonly streamUrl: string
16
+ readonly streamHeaders: Record<string, string>
17
+ private sandbox
18
+ private writer
19
+ private agentEventCallbacks
20
+ private completeCallbacks
21
+ private closed
22
+ private cmdId
23
+ private stdoutBuffer
24
+ constructor(sessionId: string, connection: StreamConnectionInfo, sandbox: Sandbox)
25
+ emit(event: EngineEvent): Promise<void>
26
+ sendCommand(cmd: Record<string, unknown>): Promise<void>
27
+ sendGateResponse(gate: string, value: Record<string, unknown>): Promise<void>
28
+ onAgentEvent(cb: (event: EngineEvent) => void): void
29
+ onComplete(cb: (success: boolean) => void): void
30
+ start(): Promise<void>
31
+ private pollLogs
32
+ private handleStdout
33
+ close(): void
34
34
  }
35
- //# sourceMappingURL=daytona.d.ts.map
35
+ //# sourceMappingURL=daytona.d.ts.map