juno-code 1.0.34 → 1.0.35

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.
package/dist/index.js CHANGED
@@ -75,7 +75,7 @@ var __export = (target, all) => {
75
75
  exports.version = void 0;
76
76
  var init_version = __esm({
77
77
  "src/version.ts"() {
78
- exports.version = "1.0.34";
78
+ exports.version = "1.0.35";
79
79
  }
80
80
  });
81
81
  function isHeadlessEnvironment() {
package/dist/index.mjs CHANGED
@@ -44,7 +44,7 @@ var __export = (target, all) => {
44
44
  var version;
45
45
  var init_version = __esm({
46
46
  "src/version.ts"() {
47
- version = "1.0.34";
47
+ version = "1.0.35";
48
48
  }
49
49
  });
50
50
  function isHeadlessEnvironment() {
@@ -34,6 +34,7 @@ class CodexService:
34
34
  self.prompt = ""
35
35
  self.additional_args: List[str] = []
36
36
  self.verbose = False
37
+ self._item_counter = 0
37
38
 
38
39
  def expand_model_shorthand(self, model: str) -> str:
39
40
  """
@@ -199,6 +200,37 @@ Environment Variables:
199
200
  content_text,
200
201
  )
201
202
 
203
+ def _parse_item_number(self, item_id: str) -> Optional[int]:
204
+ """Return numeric component from item_{n} ids or None if unparseable."""
205
+ if not isinstance(item_id, str):
206
+ return None
207
+ item_id = item_id.strip()
208
+ if not item_id.startswith("item_"):
209
+ return None
210
+ try:
211
+ return int(item_id.split("item_", 1)[1])
212
+ except Exception:
213
+ return None
214
+
215
+ def _normalize_item_id(self, payload: dict, outer_type: str) -> Optional[str]:
216
+ """
217
+ Prefer the existing id on item.* payloads; otherwise synthesize sequential item_{n}.
218
+ Maintains a per-run counter so missing ids still expose turn counts.
219
+ """
220
+ item_id = payload.get("id") if isinstance(payload, dict) else None
221
+ if isinstance(item_id, str) and item_id.strip():
222
+ parsed = self._parse_item_number(item_id)
223
+ if parsed is not None and parsed + 1 > self._item_counter:
224
+ self._item_counter = parsed + 1
225
+ return item_id.strip()
226
+
227
+ if isinstance(outer_type, str) and outer_type.startswith("item."):
228
+ generated = f"item_{self._item_counter}"
229
+ self._item_counter += 1
230
+ return generated
231
+
232
+ return None
233
+
202
234
  def read_prompt_file(self, file_path: str) -> str:
203
235
  """Read prompt from a file"""
204
236
  try:
@@ -266,6 +298,7 @@ Environment Variables:
266
298
  msg_type: str,
267
299
  payload: dict,
268
300
  outer_type: str = "",
301
+ item_id: Optional[str] = None,
269
302
  ) -> Optional[str]:
270
303
  """
271
304
  Pretty format for specific msg types to be human readable while
@@ -282,12 +315,21 @@ Environment Variables:
282
315
  now = datetime.now().strftime("%I:%M:%S %p")
283
316
  msg_type = (msg_type or "").strip()
284
317
  header_type = (outer_type or msg_type).strip()
285
- header = {"type": header_type or msg_type or "message", "datetime": now}
318
+ base_type = header_type or msg_type or "message"
286
319
 
287
- if outer_type and msg_type and outer_type != msg_type:
288
- header["item_type"] = msg_type
320
+ def make_header(type_value: str):
321
+ hdr = {"type": type_value, "datetime": now}
322
+ if item_id:
323
+ hdr["id"] = item_id
324
+ if outer_type and msg_type and outer_type != msg_type:
325
+ hdr["item_type"] = msg_type
326
+ return hdr
327
+
328
+ header = make_header(base_type)
289
329
 
290
330
  if isinstance(payload, dict):
331
+ if item_id and "id" not in payload:
332
+ payload["id"] = item_id
291
333
  if payload.get("command"):
292
334
  header["command"] = payload.get("command")
293
335
  if payload.get("status"):
@@ -298,9 +340,7 @@ Environment Variables:
298
340
  # agent_reasoning → show 'text' human-readable
299
341
  if msg_type in {"agent_reasoning", "reasoning"}:
300
342
  content = self._extract_reasoning_text(payload)
301
- header = {"type": header_type or msg_type, "datetime": now}
302
- if outer_type and msg_type and outer_type != msg_type:
303
- header["item_type"] = msg_type
343
+ header = make_header(header_type or msg_type)
304
344
  if "\n" in content:
305
345
  return json.dumps(header, ensure_ascii=False) + "\ntext:\n" + content
306
346
  header["text"] = content
@@ -308,9 +348,7 @@ Environment Variables:
308
348
 
309
349
  if msg_type in {"agent_message", "message", "assistant_message", "assistant"}:
310
350
  content = self._extract_message_text(payload)
311
- header = {"type": header_type or msg_type, "datetime": now}
312
- if outer_type and msg_type and outer_type != msg_type:
313
- header["item_type"] = msg_type
351
+ header = make_header(header_type or msg_type)
314
352
  if "\n" in content:
315
353
  return json.dumps(header, ensure_ascii=False) + "\nmessage:\n" + content
316
354
  if content != "":
@@ -386,6 +424,9 @@ Environment Variables:
386
424
  parts = [p.strip() for p in env_val.split(",") if p.strip()]
387
425
  hide_types.update(parts)
388
426
 
427
+ # Reset per-run item counter for synthesized ids
428
+ self._item_counter = 0
429
+
389
430
  # We fully suppress all token_count events (do not emit even at end)
390
431
  last_token_count = None
391
432
 
@@ -444,6 +485,14 @@ Environment Variables:
444
485
  def handle_obj(obj_dict: dict):
445
486
  nonlocal last_token_count
446
487
  msg_type_inner, payload_inner, outer_type_inner = self._normalize_event(obj_dict)
488
+ item_id_inner = self._normalize_item_id(payload_inner, outer_type_inner)
489
+
490
+ if (
491
+ item_id_inner
492
+ and isinstance(obj_dict.get("item"), dict)
493
+ and not obj_dict["item"].get("id")
494
+ ):
495
+ obj_dict["item"]["id"] = item_id_inner
447
496
 
448
497
  if msg_type_inner == "token_count":
449
498
  last_token_count = obj_dict
@@ -452,7 +501,12 @@ Environment Variables:
452
501
  if msg_type_inner and msg_type_inner in hide_types:
453
502
  return # suppress
454
503
 
455
- pretty_line_inner = self._format_msg_pretty(msg_type_inner, payload_inner, outer_type_inner)
504
+ pretty_line_inner = self._format_msg_pretty(
505
+ msg_type_inner,
506
+ payload_inner,
507
+ outer_type_inner,
508
+ item_id=item_id_inner,
509
+ )
456
510
  if pretty_line_inner is not None:
457
511
  print(pretty_line_inner, flush=True)
458
512
  else:
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "juno-code",
3
- "version": "1.0.34",
3
+ "version": "1.0.35",
4
4
  "description": "TypeScript CLI tool for AI subagent orchestration with code automation",
5
5
  "keywords": [
6
6
  "ai",