@oh-my-pi/pi-coding-agent 16.1.12 → 16.1.14

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 (130) hide show
  1. package/CHANGELOG.md +27 -0
  2. package/dist/cli.js +5650 -2860
  3. package/dist/types/config/settings-schema.d.ts +44 -14
  4. package/dist/types/cursor.d.ts +9 -9
  5. package/dist/types/eval/__tests__/julia-prelude.test.d.ts +1 -0
  6. package/dist/types/eval/backend-helpers.d.ts +23 -0
  7. package/dist/types/eval/executor-base.d.ts +118 -0
  8. package/dist/types/eval/index.d.ts +2 -0
  9. package/dist/types/eval/jl/executor.d.ts +44 -0
  10. package/dist/types/eval/jl/index.d.ts +11 -0
  11. package/dist/types/eval/jl/kernel.d.ts +28 -0
  12. package/dist/types/eval/jl/prelude.d.ts +1 -0
  13. package/dist/types/eval/jl/runtime.d.ts +22 -0
  14. package/dist/types/eval/kernel-base.d.ts +105 -0
  15. package/dist/types/eval/py/kernel.d.ts +3 -61
  16. package/dist/types/eval/rb/executor.d.ts +77 -0
  17. package/dist/types/eval/rb/index.d.ts +11 -0
  18. package/dist/types/eval/rb/kernel.d.ts +31 -0
  19. package/dist/types/eval/rb/prelude.d.ts +1 -0
  20. package/dist/types/eval/rb/runtime.d.ts +23 -0
  21. package/dist/types/eval/runtime-env.d.ts +24 -0
  22. package/dist/types/eval/types.d.ts +3 -3
  23. package/dist/types/extensibility/extensions/runner.d.ts +2 -15
  24. package/dist/types/extensibility/hooks/loader.d.ts +1 -25
  25. package/dist/types/extensibility/session-handler-types.d.ts +23 -0
  26. package/dist/types/jsonrpc/message-framing.d.ts +35 -0
  27. package/dist/types/mnemopi/embed-client.d.ts +7 -24
  28. package/dist/types/modes/components/chat-transcript-builder.d.ts +1 -1
  29. package/dist/types/modes/components/custom-editor.d.ts +11 -0
  30. package/dist/types/modes/components/selector-helpers.d.ts +53 -0
  31. package/dist/types/modes/interactive-mode.d.ts +0 -2
  32. package/dist/types/modes/theme/theme.d.ts +8 -1
  33. package/dist/types/modes/types.d.ts +0 -2
  34. package/dist/types/modes/utils/copy-targets.d.ts +1 -1
  35. package/dist/types/modes/utils/interactive-context-helpers.d.ts +14 -0
  36. package/dist/types/modes/utils/transcript-render-helpers.d.ts +54 -0
  37. package/dist/types/sdk.d.ts +1 -1
  38. package/dist/types/session/agent-session.d.ts +2 -2
  39. package/dist/types/stt/asr-client.d.ts +3 -29
  40. package/dist/types/subprocess/worker-client.d.ts +149 -0
  41. package/dist/types/subprocess/worker-runtime.d.ts +107 -0
  42. package/dist/types/tiny/title-client.d.ts +14 -34
  43. package/dist/types/tools/eval-backends.d.ts +6 -3
  44. package/dist/types/tools/eval-render.d.ts +6 -5
  45. package/dist/types/tools/eval.d.ts +13 -15
  46. package/dist/types/tools/index.d.ts +3 -3
  47. package/dist/types/tts/tts-client.d.ts +3 -28
  48. package/dist/types/tui/code-cell.d.ts +7 -0
  49. package/dist/types/utils/file-display-mode.d.ts +1 -1
  50. package/dist/types/web/parallel.d.ts +6 -0
  51. package/package.json +12 -12
  52. package/src/config/settings-schema.ts +50 -18
  53. package/src/config/settings.ts +5 -0
  54. package/src/dap/client.ts +13 -107
  55. package/src/eval/__tests__/julia-prelude.test.ts +77 -0
  56. package/src/eval/backend-helpers.ts +48 -0
  57. package/src/eval/executor-base.ts +425 -0
  58. package/src/eval/index.ts +2 -0
  59. package/src/eval/jl/executor.ts +540 -0
  60. package/src/eval/jl/index.ts +54 -0
  61. package/src/eval/jl/kernel.ts +235 -0
  62. package/src/eval/jl/prelude.jl +930 -0
  63. package/src/eval/jl/prelude.ts +3 -0
  64. package/src/eval/jl/runner.jl +634 -0
  65. package/src/eval/jl/runtime.ts +118 -0
  66. package/src/eval/js/index.ts +3 -14
  67. package/src/eval/kernel-base.ts +569 -0
  68. package/src/eval/py/executor.ts +43 -252
  69. package/src/eval/py/index.ts +9 -20
  70. package/src/eval/py/kernel.ts +29 -544
  71. package/src/eval/rb/executor.ts +504 -0
  72. package/src/eval/rb/index.ts +54 -0
  73. package/src/eval/rb/kernel.ts +230 -0
  74. package/src/eval/rb/prelude.rb +721 -0
  75. package/src/eval/rb/prelude.ts +3 -0
  76. package/src/eval/rb/runner.rb +474 -0
  77. package/src/eval/rb/runtime.ts +132 -0
  78. package/src/eval/runtime-env.ts +104 -0
  79. package/src/eval/types.ts +3 -3
  80. package/src/extensibility/extensions/runner.ts +4 -11
  81. package/src/extensibility/hooks/loader.ts +3 -21
  82. package/src/extensibility/session-handler-types.ts +21 -0
  83. package/src/internal-urls/docs-index.generated.txt +1 -1
  84. package/src/jsonrpc/message-framing.ts +142 -0
  85. package/src/lsp/client.ts +13 -109
  86. package/src/mnemopi/embed-client.ts +43 -198
  87. package/src/modes/components/agent-dashboard.ts +17 -40
  88. package/src/modes/components/chat-transcript-builder.ts +18 -102
  89. package/src/modes/components/custom-editor.ts +19 -0
  90. package/src/modes/components/extensions/extension-dashboard.ts +4 -13
  91. package/src/modes/components/extensions/extension-list.ts +16 -44
  92. package/src/modes/components/history-search.ts +4 -16
  93. package/src/modes/components/selector-helpers.ts +129 -0
  94. package/src/modes/components/settings-selector.ts +7 -5
  95. package/src/modes/components/tree-selector.ts +13 -18
  96. package/src/modes/controllers/event-controller.ts +3 -9
  97. package/src/modes/controllers/input-controller.ts +32 -54
  98. package/src/modes/interactive-mode.ts +5 -7
  99. package/src/modes/theme/theme.ts +35 -0
  100. package/src/modes/types.ts +0 -2
  101. package/src/modes/utils/copy-targets.ts +3 -2
  102. package/src/modes/utils/interactive-context-helpers.ts +27 -0
  103. package/src/modes/utils/transcript-render-helpers.ts +157 -0
  104. package/src/modes/utils/ui-helpers.ts +21 -126
  105. package/src/prompts/system/system-prompt.md +10 -10
  106. package/src/prompts/tools/bash.md +0 -1
  107. package/src/prompts/tools/eval.md +6 -4
  108. package/src/prompts/tools/find.md +0 -4
  109. package/src/prompts/tools/read.md +1 -2
  110. package/src/prompts/tools/replace.md +1 -1
  111. package/src/prompts/tools/search.md +0 -1
  112. package/src/sdk.ts +13 -7
  113. package/src/session/agent-session.ts +9 -7
  114. package/src/stt/asr-client.ts +35 -215
  115. package/src/stt/asr-worker.ts +29 -181
  116. package/src/subprocess/worker-client.ts +297 -0
  117. package/src/subprocess/worker-runtime.ts +277 -0
  118. package/src/task/executor.ts +4 -4
  119. package/src/tiny/title-client.ts +53 -219
  120. package/src/tiny/worker.ts +29 -180
  121. package/src/tools/eval-backends.ts +10 -3
  122. package/src/tools/eval-render.ts +17 -8
  123. package/src/tools/eval.ts +187 -22
  124. package/src/tools/index.ts +51 -28
  125. package/src/tts/tts-client.ts +38 -206
  126. package/src/tts/tts-worker.ts +23 -97
  127. package/src/tui/code-cell.ts +12 -1
  128. package/src/utils/file-display-mode.ts +2 -3
  129. package/src/web/parallel.ts +43 -42
  130. package/src/web/search/providers/parallel.ts +10 -99
@@ -0,0 +1,3 @@
1
+ import juliaPrelude from "./prelude.jl" with { type: "text" };
2
+
3
+ export const JULIA_PRELUDE = juliaPrelude;
@@ -0,0 +1,634 @@
1
+ # OMP Julia runner — subprocess wrapper used by the coding-agent host.
2
+ # Persistent Julia process that speaks NDJSON over stdout and a custom TSV protocol on stdin.
3
+
4
+ using Base64
5
+
6
+ const ORIGINAL_STDOUT = stdout
7
+ const ORIGINAL_STDERR = stderr
8
+ const ORIGINAL_STDIN = stdin
9
+
10
+ # Redirect stdin/stdout/stderr to prevent cell prints from corrupting NDJSON
11
+ out_rd, out_wr = redirect_stdout()
12
+ err_rd, err_wr = redirect_stderr()
13
+ redirect_stdin(devnull)
14
+
15
+ global current_rid = nothing
16
+ const write_lock = ReentrantLock()
17
+ const drain_state_lock = ReentrantLock()
18
+
19
+ mutable struct DrainBarrier
20
+ marker::Vector{UInt8}
21
+ remaining::Int
22
+ done::Channel{Nothing}
23
+ end
24
+
25
+ const active_drain_barrier = Ref{Union{Nothing, DrainBarrier}}(nothing)
26
+ const drain_marker_counter = Ref{UInt}(0)
27
+
28
+ function json_parse(s::String)
29
+ chars = collect(s)
30
+ pos = 1
31
+ len = length(chars)
32
+
33
+ function skip_whitespace()
34
+ while pos <= len && isspace(chars[pos])
35
+ pos += 1
36
+ end
37
+ end
38
+
39
+ function parse_value()
40
+ skip_whitespace()
41
+ if pos > len
42
+ error("Unexpected EOF")
43
+ end
44
+ c = chars[pos]
45
+ if c == '"'
46
+ return parse_string()
47
+ elseif c == '{'
48
+ return parse_object()
49
+ elseif c == '['
50
+ return parse_array()
51
+ elseif (c == 't' || c == 'f')
52
+ return parse_boolean()
53
+ elseif c == 'n'
54
+ return parse_null()
55
+ elseif c == '-' || isdigit(c)
56
+ return parse_number()
57
+ else
58
+ error("Unexpected character at $pos: $c")
59
+ end
60
+ end
61
+
62
+ function parse_string()
63
+ pos += 1 # skip '"'
64
+ res = IOBuffer()
65
+ while pos <= len
66
+ c = chars[pos]
67
+ if c == '"'
68
+ pos += 1 # skip '"'
69
+ return String(take!(res))
70
+ elseif c == '\\'
71
+ pos += 1
72
+ if pos > len; error("Unexpected EOF in string escape"); end
73
+ esc = chars[pos]
74
+ if esc == '"'
75
+ write(res, '"')
76
+ elseif esc == '\\'
77
+ write(res, '\\')
78
+ elseif esc == '/'
79
+ write(res, '/')
80
+ elseif esc == 'b'
81
+ write(res, '\b')
82
+ elseif esc == 'f'
83
+ write(res, '\f')
84
+ elseif esc == 'n'
85
+ write(res, '\n')
86
+ elseif esc == 'r'
87
+ write(res, '\r')
88
+ elseif esc == 't'
89
+ write(res, '\t')
90
+ elseif esc == 'u'
91
+ # parse 4 hex digits
92
+ hex = ""
93
+ for i in 1:4
94
+ pos += 1
95
+ hex *= chars[pos]
96
+ end
97
+ write(res, Char(parse(Int, hex, base=16)))
98
+ else
99
+ write(res, esc)
100
+ end
101
+ else
102
+ write(res, c)
103
+ end
104
+ pos += 1
105
+ end
106
+ error("Unterminated string")
107
+ end
108
+
109
+ function parse_object()
110
+ pos += 1 # skip '{'
111
+ obj = Dict{String, Any}()
112
+ skip_whitespace()
113
+ if pos <= len && chars[pos] == '}'
114
+ pos += 1
115
+ return obj
116
+ end
117
+ while true
118
+ skip_whitespace()
119
+ if pos > len || chars[pos] != '"'
120
+ error("Expected string key in object at $pos")
121
+ end
122
+ key = parse_string()
123
+ skip_whitespace()
124
+ if pos > len || chars[pos] != ':'
125
+ error("Expected ':' at $pos")
126
+ end
127
+ pos += 1 # skip ':'
128
+ val = parse_value()
129
+ obj[key] = val
130
+ skip_whitespace()
131
+ if pos > len
132
+ error("Expected ',' or '}' in object at $pos")
133
+ end
134
+ c = chars[pos]
135
+ if c == '}'
136
+ pos += 1
137
+ return obj
138
+ elseif c == ','
139
+ pos += 1
140
+ else
141
+ error("Expected ',' or '}' in object at $pos, got '$c'")
142
+ end
143
+ end
144
+ end
145
+
146
+ function parse_array()
147
+ pos += 1 # skip '['
148
+ arr = Any[]
149
+ skip_whitespace()
150
+ if pos <= len && chars[pos] == ']'
151
+ pos += 1
152
+ return arr
153
+ end
154
+ while true
155
+ push!(arr, parse_value())
156
+ skip_whitespace()
157
+ if pos > len
158
+ error("Expected ',' or ']' in array")
159
+ end
160
+ c = chars[pos]
161
+ if c == ']'
162
+ pos += 1
163
+ return arr
164
+ elseif c == ','
165
+ pos += 1
166
+ else
167
+ error("Expected ',' or ']' in array at $pos, got '$c'")
168
+ end
169
+ end
170
+ end
171
+
172
+ function parse_boolean()
173
+ s_slice = String(chars[pos:min(len, pos+4)])
174
+ if startswith(s_slice, "true")
175
+ pos += 4
176
+ return true
177
+ elseif startswith(s_slice, "false")
178
+ pos += 5
179
+ return false
180
+ else
181
+ error("Expected boolean at $pos")
182
+ end
183
+ end
184
+
185
+ function parse_null()
186
+ s_slice = String(chars[pos:min(len, pos+3)])
187
+ if startswith(s_slice, "null")
188
+ pos += 4
189
+ return nothing
190
+ else
191
+ error("Expected null at $pos")
192
+ end
193
+ end
194
+
195
+ function parse_number()
196
+ start_pos = pos
197
+ while pos <= len
198
+ c = chars[pos]
199
+ if isdigit(c) || c in ['.', '-', '+', 'e', 'E']
200
+ pos += 1
201
+ else
202
+ break
203
+ end
204
+ end
205
+ num_str = String(chars[start_pos:pos-1])
206
+ val = tryparse(Int, num_str)
207
+ if val !== nothing
208
+ return val
209
+ end
210
+ val_f = tryparse(Float64, num_str)
211
+ if val_f !== nothing
212
+ return val_f
213
+ end
214
+ error("Invalid number format: $num_str")
215
+ end
216
+
217
+ val = parse_value()
218
+ skip_whitespace()
219
+ if pos <= len
220
+ error("Extra data after JSON value at $pos")
221
+ end
222
+ return val
223
+ end
224
+
225
+ function json_serialize_string(s::AbstractString)
226
+ res = IOBuffer()
227
+ write(res, '"')
228
+ for c in s
229
+ if c == '"'
230
+ write(res, "\\\"")
231
+ elseif c == '\\'
232
+ write(res, "\\\\")
233
+ elseif c == '\n'
234
+ write(res, "\\n")
235
+ elseif c == '\r'
236
+ write(res, "\\r")
237
+ elseif c == '\t'
238
+ write(res, "\\t")
239
+ elseif c == '\f'
240
+ write(res, "\\f")
241
+ elseif c == '\b'
242
+ write(res, "\\b")
243
+ elseif UInt32(c) < 32
244
+ d1 = div(UInt32(c), 16)
245
+ d2 = rem(UInt32(c), 16)
246
+ hex_chars = "0123456789abcdef"
247
+ write(res, "\\u00" * hex_chars[d1 + 1] * hex_chars[d2 + 1])
248
+ else
249
+ write(res, c)
250
+ end
251
+ end
252
+ write(res, '"')
253
+ return String(take!(res))
254
+ end
255
+
256
+ function json_serialize(v)
257
+ if v === nothing
258
+ return "null"
259
+ elseif v isa Bool
260
+ return v ? "true" : "false"
261
+ elseif v isa Number
262
+ return string(v)
263
+ elseif v isa AbstractString
264
+ return json_serialize_string(v)
265
+ elseif v isa Symbol
266
+ return json_serialize_string(string(v))
267
+ elseif v isa AbstractVector
268
+ return "[" * join([json_serialize(x) for x in v], ",") * "]"
269
+ elseif v isa AbstractDict
270
+ parts = String[]
271
+ for (k, val) in v
272
+ push!(parts, json_serialize_string(string(k)) * ":" * json_serialize(val))
273
+ end
274
+ return "{" * join(parts, ",") * "}"
275
+ else
276
+ return json_serialize_string(repr(v))
277
+ end
278
+ end
279
+
280
+ function emit_frame(frame)
281
+ lock(write_lock) do
282
+ println(ORIGINAL_STDOUT, json_serialize(frame))
283
+ flush(ORIGINAL_STDOUT)
284
+ end
285
+ end
286
+
287
+ function find_subsequence(haystack::Vector{UInt8}, needle::Vector{UInt8})
288
+ needle_len = length(needle)
289
+ if needle_len == 0 || length(haystack) < needle_len
290
+ return nothing
291
+ end
292
+ last_start = length(haystack) - needle_len + 1
293
+ for start in 1:last_start
294
+ matched = true
295
+ @inbounds for offset in 1:needle_len
296
+ if haystack[start + offset - 1] != needle[offset]
297
+ matched = false
298
+ break
299
+ end
300
+ end
301
+ if matched
302
+ return start
303
+ end
304
+ end
305
+ return nothing
306
+ end
307
+
308
+ function marker_overlap(haystack::Vector{UInt8}, needle::Vector{UInt8})
309
+ max_overlap = min(length(haystack), length(needle) - 1)
310
+ for overlap in max_overlap:-1:1
311
+ matched = true
312
+ @inbounds for offset in 1:overlap
313
+ if haystack[length(haystack) - overlap + offset] != needle[offset]
314
+ matched = false
315
+ break
316
+ end
317
+ end
318
+ if matched
319
+ return overlap
320
+ end
321
+ end
322
+ return 0
323
+ end
324
+
325
+ function emit_stream_bytes(kind, bytes::Vector{UInt8})
326
+ rid = current_rid
327
+ if rid === nothing || isempty(bytes)
328
+ return
329
+ end
330
+ emit_frame(Dict("type" => kind, "id" => rid, "data" => String(copy(bytes))))
331
+ end
332
+
333
+ function signal_drain_barrier!(marker::Vector{UInt8})
334
+ done_channel = lock(drain_state_lock) do
335
+ barrier = active_drain_barrier[]
336
+ if barrier === nothing || barrier.marker != marker
337
+ return nothing
338
+ end
339
+ barrier.remaining -= 1
340
+ if barrier.remaining == 0
341
+ active_drain_barrier[] = nothing
342
+ return barrier.done
343
+ end
344
+ return nothing
345
+ end
346
+ if done_channel !== nothing
347
+ put!(done_channel, nothing)
348
+ end
349
+ return nothing
350
+ end
351
+
352
+ function process_stream_buffer!(buffer::Vector{UInt8}, kind; flush_all::Bool=false)
353
+ while true
354
+ barrier = lock(drain_state_lock) do
355
+ active_drain_barrier[]
356
+ end
357
+ marker = barrier === nothing ? nothing : barrier.marker
358
+ if marker === nothing
359
+ if !isempty(buffer)
360
+ emit_stream_bytes(kind, buffer)
361
+ empty!(buffer)
362
+ end
363
+ return
364
+ end
365
+
366
+ marker_index = find_subsequence(buffer, marker)
367
+ if marker_index !== nothing
368
+ emit_len = marker_index - 1
369
+ if emit_len > 0
370
+ emit_stream_bytes(kind, buffer[1:emit_len])
371
+ end
372
+ deleteat!(buffer, 1:(marker_index + length(marker) - 1))
373
+ signal_drain_barrier!(marker)
374
+ continue
375
+ end
376
+
377
+ keep_len = flush_all ? 0 : marker_overlap(buffer, marker)
378
+ emit_len = length(buffer) - keep_len
379
+ if emit_len > 0
380
+ emit_stream_bytes(kind, buffer[1:emit_len])
381
+ deleteat!(buffer, 1:emit_len)
382
+ end
383
+ return
384
+ end
385
+ end
386
+
387
+ function next_drain_marker()
388
+ drain_marker_counter[] += UInt(1)
389
+ return Vector{UInt8}(codeunits("\0__OMP_DRAIN__:" * string(drain_marker_counter[]) * ":" * string(time_ns()) * "\0"))
390
+ end
391
+
392
+ function await_stream_drains()
393
+ flush(stdout)
394
+ flush(stderr)
395
+ barrier = DrainBarrier(next_drain_marker(), 2, Channel{Nothing}(1))
396
+ lock(drain_state_lock) do
397
+ if active_drain_barrier[] !== nothing
398
+ error("Drain barrier already active")
399
+ end
400
+ active_drain_barrier[] = barrier
401
+ end
402
+ try
403
+ Base.write(out_wr, barrier.marker)
404
+ flush(out_wr)
405
+ Base.write(err_wr, barrier.marker)
406
+ flush(err_wr)
407
+ take!(barrier.done)
408
+ finally
409
+ lock(drain_state_lock) do
410
+ if active_drain_barrier[] === barrier
411
+ active_drain_barrier[] = nothing
412
+ end
413
+ end
414
+ end
415
+ return nothing
416
+ end
417
+
418
+ function drain_stream(rd, kind)
419
+ buffer = UInt8[]
420
+ try
421
+ while true
422
+ data = readavailable(rd)
423
+ if !isempty(data)
424
+ append!(buffer, data)
425
+ process_stream_buffer!(buffer, kind)
426
+ elseif eof(rd)
427
+ break
428
+ else
429
+ sleep(0.001)
430
+ end
431
+ end
432
+ catch
433
+ # ignore
434
+ finally
435
+ process_stream_buffer!(buffer, kind, flush_all=true)
436
+ end
437
+ end
438
+
439
+ @async drain_stream(out_rd, "stdout")
440
+ @async drain_stream(err_rd, "stderr")
441
+
442
+ function build_mime_bundle(value)
443
+ bundle = Dict{String, Any}()
444
+
445
+ # text/plain
446
+ io_plain = IOBuffer()
447
+ show(io_plain, MIME"text/plain"(), value)
448
+ bundle["text/plain"] = String(take!(io_plain))
449
+
450
+ # rich mime types
451
+ for mime_str in ["text/html", "text/markdown", "image/png", "image/jpeg"]
452
+ m = MIME(Symbol(mime_str))
453
+ if showable(m, value)
454
+ try
455
+ io = IOBuffer()
456
+ if mime_str in ["image/png", "image/jpeg"]
457
+ b64_io = Base64EncodePipe(io)
458
+ show(b64_io, m, value)
459
+ close(b64_io)
460
+ else
461
+ show(io, m, value)
462
+ end
463
+ bundle[mime_str] = String(take!(io))
464
+ catch
465
+ # ignore
466
+ end
467
+ end
468
+ end
469
+
470
+ if value isa AbstractDict || value isa AbstractVector
471
+ try
472
+ bundle["application/json"] = value
473
+ catch
474
+ # ignore
475
+ end
476
+ end
477
+
478
+ return bundle
479
+ end
480
+
481
+ struct OmpDisplay <: AbstractDisplay end
482
+
483
+ function Base.display(d::OmpDisplay, value)
484
+ rid = current_rid
485
+ if rid !== nothing
486
+ bundle = build_mime_bundle(value)
487
+ emit_frame(Dict("type" => "display", "id" => rid, "bundle" => bundle))
488
+ end
489
+ return nothing
490
+ end
491
+
492
+ pushdisplay(OmpDisplay())
493
+
494
+ function emit_error(rid, err, bt)
495
+ io = IOBuffer()
496
+ showerror(io, err)
497
+ err_str = String(take!(io))
498
+
499
+ tb = String[]
500
+ for frame in stacktrace(bt)
501
+ file = string(frame.file)
502
+ line = frame.line
503
+ func = string(frame.func)
504
+ push!(tb, " at $func ($file:$line)")
505
+ end
506
+
507
+ emit_frame(Dict(
508
+ "type" => "error",
509
+ "id" => rid,
510
+ "ename" => string(typeof(err)),
511
+ "evalue" => err_str,
512
+ "traceback" => tb
513
+ ))
514
+ end
515
+
516
+ function should_display_result(parsed_expr)
517
+ if parsed_expr isa Expr && parsed_expr.head === :block
518
+ args = parsed_expr.args
519
+ if !isempty(args)
520
+ last_arg = args[end]
521
+ if last_arg isa Expr
522
+ if last_arg.head in [Symbol("="), :function, :struct, :using, :import, :const, :global, :local, :macro]
523
+ return false
524
+ end
525
+ end
526
+ end
527
+ end
528
+ return true
529
+ end
530
+
531
+ function apply_request_runtime(cwd, env_pairs)
532
+ try
533
+ if !isempty(cwd)
534
+ cd(cwd)
535
+ end
536
+ catch
537
+ # ignore
538
+ end
539
+
540
+ managed_env_keys = [
541
+ "PI_SESSION_FILE",
542
+ "PI_ARTIFACTS_DIR",
543
+ "PI_TOOL_BRIDGE_URL",
544
+ "PI_TOOL_BRIDGE_TOKEN",
545
+ "PI_TOOL_BRIDGE_SESSION",
546
+ "PI_EVAL_LOCAL_ROOTS"
547
+ ]
548
+ for k in managed_env_keys
549
+ delete!(ENV, k)
550
+ end
551
+
552
+ if !isempty(env_pairs)
553
+ for pair in split(env_pairs, ' ')
554
+ if !isempty(pair)
555
+ try
556
+ k_b64, v_b64 = split(pair, ':', limit=2)
557
+ k = String(base64decode(string(k_b64)))
558
+ v = String(base64decode(string(v_b64)))
559
+ ENV[k] = v
560
+ catch
561
+ # ignore
562
+ end
563
+ end
564
+ end
565
+ end
566
+ end
567
+
568
+ # Main loop
569
+ function main()
570
+ while !eof(ORIGINAL_STDIN)
571
+ line = readline(ORIGINAL_STDIN)
572
+ if isempty(line)
573
+ continue
574
+ end
575
+ parts = split(line, '\t')
576
+ cmd = string(parts[1])
577
+ if cmd == "exit"
578
+ break
579
+ elseif cmd == "run"
580
+ if length(parts) < 7
581
+ continue
582
+ end
583
+ rid = string(parts[2])
584
+ cwd = String(base64decode(string(parts[3])))
585
+ silent = string(parts[4]) == "1"
586
+ store_history = string(parts[5]) == "1"
587
+ env_pairs = string(parts[6])
588
+ code = String(base64decode(string(parts[7])))
589
+
590
+ global current_rid = rid
591
+ emit_frame(Dict("type" => "started", "id" => rid))
592
+
593
+ apply_request_runtime(cwd, env_pairs)
594
+
595
+ exec_status = "ok"
596
+ try
597
+ parsed = Meta.parse("begin\n" * code * "\nend")
598
+ if parsed isa Expr && parsed.head === :error
599
+ # Syntax error from parser
600
+ exec_status = "error"
601
+ emit_frame(Dict(
602
+ "type" => "error",
603
+ "id" => rid,
604
+ "ename" => "ParseError",
605
+ "evalue" => string(parsed.args[1]),
606
+ "traceback" => String[]
607
+ ))
608
+ else
609
+ ans = Core.eval(Main, parsed)
610
+ if ans !== nothing && !silent && should_display_result(parsed)
611
+ bundle = build_mime_bundle(ans)
612
+ emit_frame(Dict("type" => "result", "id" => rid, "bundle" => bundle))
613
+ end
614
+ end
615
+ catch err
616
+ exec_status = "error"
617
+ emit_error(rid, err, catch_backtrace())
618
+ end
619
+
620
+ await_stream_drains()
621
+
622
+ emit_frame(Dict(
623
+ "type" => "done",
624
+ "id" => rid,
625
+ "status" => exec_status,
626
+ "executionCount" => 1,
627
+ "cancelled" => false
628
+ ))
629
+ global current_rid = nothing
630
+ end
631
+ end
632
+ end
633
+
634
+ main()