promptlayer 1.2.1 → 1.2.2

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 (29) hide show
  1. package/dist/claude-agents.js +1 -1
  2. package/dist/claude-agents.js.map +1 -1
  3. package/dist/esm/{chunk-UKSCOWKT.js → chunk-7Y65WGSZ.js} +2 -2
  4. package/dist/esm/claude-agents.js +1 -1
  5. package/dist/esm/claude-agents.js.map +1 -1
  6. package/dist/esm/index.js +1 -1
  7. package/dist/esm/openai-agents.js +1 -1
  8. package/dist/index.js +1 -1
  9. package/dist/openai-agents.js +1 -1
  10. package/package.json +1 -1
  11. package/vendor/claude-agents/trace/hooks/lib.sh +4 -522
  12. package/vendor/claude-agents/trace/hooks/post_tool_use.sh +2 -27
  13. package/vendor/claude-agents/trace/hooks/py/__init__.py +1 -0
  14. package/vendor/claude-agents/trace/hooks/py/cli.py +81 -0
  15. package/vendor/claude-agents/trace/hooks/py/context.py +63 -0
  16. package/vendor/claude-agents/trace/hooks/py/handlers.py +244 -0
  17. package/vendor/claude-agents/trace/hooks/py/otlp.py +278 -0
  18. package/vendor/claude-agents/trace/hooks/py/settings.py +33 -0
  19. package/vendor/claude-agents/trace/hooks/py/state.py +135 -0
  20. package/vendor/claude-agents/trace/hooks/{parse_stop_transcript.py → py/stop_parser.py} +69 -31
  21. package/vendor/claude-agents/trace/hooks/py/traceparent.py +31 -0
  22. package/vendor/claude-agents/trace/hooks/session_end.sh +1 -23
  23. package/vendor/claude-agents/trace/hooks/session_start.sh +5 -41
  24. package/vendor/claude-agents/trace/hooks/stop_hook.sh +3 -106
  25. package/vendor/claude-agents/trace/hooks/user_prompt_submit.sh +1 -11
  26. package/vendor/claude-agents/trace/setup.sh +170 -0
  27. package/vendor/claude-agents/vendor_metadata.json +2 -2
  28. package/vendor/claude-agents/trace/hooks/hook_utils.py +0 -38
  29. /package/dist/esm/{chunk-UKSCOWKT.js.map → chunk-7Y65WGSZ.js.map} +0 -0
@@ -0,0 +1,135 @@
1
+ from dataclasses import dataclass
2
+ import json
3
+ import os
4
+ import shutil
5
+ import time
6
+
7
+ from traceparent import parse_traceparent
8
+
9
+
10
+ @dataclass
11
+ class SessionState:
12
+ trace_id: str = ""
13
+ session_span_id: str = ""
14
+ session_parent_span_id: str = ""
15
+ session_start_ns: str = ""
16
+ current_turn_start_ns: str = ""
17
+ pending_tool_calls: str = ""
18
+ session_init_source: str = ""
19
+ session_traceparent_version: str = ""
20
+ session_trace_flags: str = ""
21
+ trace_context_source: str = ""
22
+
23
+ @classmethod
24
+ def from_dict(cls, data):
25
+ if not isinstance(data, dict):
26
+ return cls()
27
+ return cls(**{field: str(data.get(field, "")) for field in cls.__dataclass_fields__})
28
+
29
+ def to_dict(self):
30
+ return {field: getattr(self, field, "") for field in self.__dataclass_fields__}
31
+
32
+
33
+ def compact_json(value) -> str:
34
+ return json.dumps(value, ensure_ascii=False, separators=(",", ":"))
35
+
36
+
37
+ def session_state_path(session_state_dir: str, session_id: str) -> str:
38
+ return os.path.join(session_state_dir, f"{session_id}.json")
39
+
40
+
41
+ def load_session_state(session_state_dir: str, session_id: str):
42
+ path = session_state_path(session_state_dir, session_id)
43
+ if not os.path.exists(path):
44
+ return SessionState(), path
45
+ try:
46
+ with open(path, encoding="utf-8") as f:
47
+ data = json.load(f)
48
+ except Exception:
49
+ return SessionState(), path
50
+ return SessionState.from_dict(data), path
51
+
52
+
53
+ def save_session_state(path: str, state: SessionState) -> None:
54
+ os.makedirs(os.path.dirname(path), exist_ok=True)
55
+ with open(path, "w", encoding="utf-8") as f:
56
+ json.dump(state.to_dict(), f, ensure_ascii=False, separators=(",", ":"))
57
+
58
+
59
+ def parse_pending_tool_calls(raw: str):
60
+ if not raw:
61
+ return []
62
+ try:
63
+ data = json.loads(raw)
64
+ except Exception:
65
+ return []
66
+ return data if isinstance(data, list) else []
67
+
68
+
69
+ def session_lock_path(lock_dir: str, session_id: str) -> str:
70
+ return os.path.join(lock_dir, f"{session_id}.lock")
71
+
72
+
73
+ def queue_lock_path(lock_dir: str) -> str:
74
+ return os.path.join(lock_dir, "queue.lock")
75
+
76
+
77
+ def acquire_lock(path: str, attempts: int = 250, sleep_seconds: float = 0.02) -> bool:
78
+ for _ in range(attempts):
79
+ try:
80
+ os.mkdir(path)
81
+ return True
82
+ except FileExistsError:
83
+ time.sleep(sleep_seconds)
84
+ except Exception:
85
+ return False
86
+ return False
87
+
88
+
89
+ def release_lock(path: str) -> None:
90
+ try:
91
+ os.rmdir(path)
92
+ except Exception:
93
+ shutil.rmtree(path, ignore_errors=True)
94
+
95
+
96
+ def ensure_session_initialized(
97
+ state: SessionState,
98
+ *,
99
+ traceparent_raw: str,
100
+ generate_trace_id,
101
+ generate_span_id,
102
+ requested_start_ns=None,
103
+ ):
104
+ if state.trace_id and state.session_span_id:
105
+ if not state.session_start_ns:
106
+ state.session_start_ns = str(requested_start_ns or time.time_ns())
107
+ if not state.session_init_source:
108
+ state.session_init_source = "unknown"
109
+ if not state.pending_tool_calls:
110
+ state.pending_tool_calls = "[]"
111
+ if not state.session_parent_span_id:
112
+ state.session_parent_span_id = ""
113
+ if not state.session_traceparent_version:
114
+ state.session_traceparent_version = ""
115
+ if not state.session_trace_flags:
116
+ state.session_trace_flags = ""
117
+ if not state.trace_context_source:
118
+ state.trace_context_source = "generated"
119
+ return state, False
120
+
121
+ trace_context = parse_traceparent(traceparent_raw)
122
+ if requested_start_ns is None:
123
+ requested_start_ns = time.time_ns()
124
+
125
+ state.trace_id = trace_context["trace_id"] if trace_context else generate_trace_id()
126
+ state.session_span_id = generate_span_id()
127
+ state.session_parent_span_id = trace_context["parent_span_id"] if trace_context else ""
128
+ state.session_start_ns = str(requested_start_ns)
129
+ state.current_turn_start_ns = ""
130
+ state.pending_tool_calls = "[]"
131
+ state.session_init_source = "lazy_init"
132
+ state.session_traceparent_version = trace_context["version"] if trace_context else ""
133
+ state.session_trace_flags = trace_context["trace_flags"] if trace_context else ""
134
+ state.trace_context_source = trace_context["source"] if trace_context else "generated"
135
+ return state, True
@@ -1,11 +1,11 @@
1
1
  #!/usr/bin/env python3
2
- """Parse a Claude transcript and return finalized turn/tool/llm spans as JSON."""
2
+ """Pure transcript parsing and stop-hook span-spec derivation."""
3
3
 
4
4
  import json
5
- import os
6
- import sys
7
5
  from datetime import datetime, timezone
8
6
 
7
+ from otlp import SpanSpec
8
+
9
9
 
10
10
  def parse_iso_to_ns(raw):
11
11
  if not raw:
@@ -172,9 +172,9 @@ def parse_transcript(transcript_path, turn_start_fallback, pending_payloads, exp
172
172
  is_error = bool(block.get("is_error", False))
173
173
 
174
174
  match_idx = None
175
- for idx, item in enumerate(pending_tool_uses):
175
+ for candidate_idx, item in enumerate(pending_tool_uses):
176
176
  if tool_use_id and item.get("id") == tool_use_id:
177
- match_idx = idx
177
+ match_idx = candidate_idx
178
178
  break
179
179
  if match_idx is None and pending_tool_uses:
180
180
  match_idx = 0
@@ -277,8 +277,6 @@ def parse_transcript(transcript_path, turn_start_fallback, pending_payloads, exp
277
277
  }
278
278
  )
279
279
 
280
- # Claude can emit intermediate assistant records that contain only
281
- # empty thinking blocks. Those should not consume the user's prompt.
282
280
  if not output_text and not tool_calls:
283
281
  continue
284
282
 
@@ -345,31 +343,71 @@ def parse_transcript(transcript_path, turn_start_fallback, pending_payloads, exp
345
343
  }
346
344
 
347
345
 
348
- def main():
349
- if len(sys.argv) < 3:
350
- print(
351
- json.dumps(
352
- {"error": "Usage: parse_stop_transcript.py <transcript_path> <turn_start_ns> [session_id]"}
346
+ def build_stop_hook_span_specs(
347
+ *,
348
+ parsed,
349
+ trace_id,
350
+ session_span_id,
351
+ session_parent_span_id,
352
+ session_start_ns,
353
+ session_init_source,
354
+ generate_span_id,
355
+ ):
356
+ turn = parsed.get("turn", {})
357
+ turn_start_ns = str(turn.get("start_ns", session_start_ns))
358
+ turn_end_ns = str(turn.get("end_ns", turn_start_ns))
359
+
360
+ if session_init_source == "lazy_init":
361
+ session_hook_attr = "StopFallback"
362
+ session_lifecycle_attr = "stop_fallback"
363
+ else:
364
+ session_hook_attr = "Stop"
365
+ session_lifecycle_attr = "in_progress"
366
+
367
+ span_specs = [
368
+ SpanSpec(
369
+ trace_id=trace_id,
370
+ span_id=session_span_id,
371
+ parent_span_id=session_parent_span_id,
372
+ name="Claude Code session",
373
+ kind="1",
374
+ start_ns=str(session_start_ns),
375
+ end_ns=turn_end_ns,
376
+ attrs={
377
+ "source": "claude-code",
378
+ "hook": session_hook_attr,
379
+ "node_type": "WORKFLOW",
380
+ "session.lifecycle": session_lifecycle_attr,
381
+ },
382
+ )
383
+ ]
384
+
385
+ for tool in parsed.get("tools", []):
386
+ span_specs.append(
387
+ SpanSpec(
388
+ trace_id=trace_id,
389
+ span_id=generate_span_id(),
390
+ parent_span_id=session_span_id,
391
+ name=tool.get("name", ""),
392
+ kind="3",
393
+ start_ns=str(tool.get("start_ns", turn_start_ns)),
394
+ end_ns=str(tool.get("end_ns", turn_end_ns)),
395
+ attrs=tool.get("attributes", {}),
353
396
  )
354
397
  )
355
- return 1
356
-
357
- transcript_path = sys.argv[1]
358
- turn_start_fallback = safe_int(sys.argv[2], 0) or None
359
- expected_session_id = sys.argv[3] if len(sys.argv) > 3 else None
360
-
361
- pending_raw = os.environ.get("PL_PENDING_TOOL_CALLS", "[]")
362
- try:
363
- pending_payloads = json.loads(pending_raw)
364
- except Exception:
365
- pending_payloads = []
366
- if not isinstance(pending_payloads, list):
367
- pending_payloads = []
368
-
369
- parsed = parse_transcript(transcript_path, turn_start_fallback, pending_payloads, expected_session_id)
370
- print(json.dumps(parsed, ensure_ascii=False, separators=(",", ":")))
371
- return 0
372
398
 
399
+ for llm in parsed.get("llms", []):
400
+ span_specs.append(
401
+ SpanSpec(
402
+ trace_id=trace_id,
403
+ span_id=generate_span_id(),
404
+ parent_span_id=session_span_id,
405
+ name=llm.get("name", ""),
406
+ kind="3",
407
+ start_ns=str(llm.get("start_ns", turn_start_ns)),
408
+ end_ns=str(llm.get("end_ns", turn_end_ns)),
409
+ attrs=llm.get("attributes", {}),
410
+ )
411
+ )
373
412
 
374
- if __name__ == "__main__":
375
- raise SystemExit(main())
413
+ return span_specs
@@ -0,0 +1,31 @@
1
+ def parse_traceparent(raw: str):
2
+ if not raw:
3
+ return None
4
+
5
+ parts = raw.lower().split("-")
6
+ if len(parts) < 4:
7
+ return None
8
+
9
+ version, trace_id, parent_span_id, trace_flags = parts[:4]
10
+ suffix = parts[4:]
11
+
12
+ if len(version) != 2 or len(trace_id) != 32 or len(parent_span_id) != 16 or len(trace_flags) != 2:
13
+ return None
14
+
15
+ hexdigits = set("0123456789abcdef")
16
+ if any(ch not in hexdigits for ch in version + trace_id + parent_span_id + trace_flags):
17
+ return None
18
+ if version == "ff":
19
+ return None
20
+ if version == "00" and suffix:
21
+ return None
22
+ if trace_id == "0" * 32 or parent_span_id == "0" * 16:
23
+ return None
24
+
25
+ return {
26
+ "version": version,
27
+ "trace_id": trace_id,
28
+ "parent_span_id": parent_span_id,
29
+ "trace_flags": trace_flags,
30
+ "source": "external_traceparent",
31
+ }
@@ -9,29 +9,7 @@ tracing_enabled || exit 0
9
9
  check_requirements || exit 0
10
10
 
11
11
  input="$(cat)"
12
- session_id="$(echo "$input" | jq -r '.session_id // empty')"
12
+ session_id="$(printf '%s' "$input" | python3 "$SCRIPT_DIR/py/cli.py" session-end)"
13
13
  [[ -z "$session_id" ]] && exit 0
14
14
 
15
- acquire_session_lock "$session_id" || exit 0
16
- trap 'release_session_lock' EXIT
17
-
18
- trace_id="$(get_session_state "$session_id" trace_id)"
19
- session_span_id="$(get_session_state "$session_id" session_span_id)"
20
- session_parent_span_id="$(get_session_state "$session_id" session_parent_span_id)"
21
- session_start_ns="$(get_session_state "$session_id" session_start_ns)"
22
- [[ -z "$trace_id" || -z "$session_span_id" ]] && exit 0
23
- [[ -z "$session_start_ns" ]] && session_start_ns="$(now_ns)"
24
-
25
- release_session_lock
26
- trap - EXIT
27
-
28
- # Always emit/re-emit root span with final end time. The server upserts on
29
- # span_id conflict, so this safely updates the end time and lifecycle attribute.
30
- end_ns="$(now_ns)"
31
- attrs='{"source":"claude-code","hook":"SessionEnd","node_type":"WORKFLOW","session.lifecycle":"complete"}'
32
- emit_span "$trace_id" "$session_span_id" "$session_parent_span_id" "Claude Code session" "1" "$session_start_ns" "$end_ns" "$attrs" || true
33
-
34
- acquire_session_lock "$session_id" || exit 0
35
- trap 'release_session_lock' EXIT
36
- rm -f "$PL_SESSION_STATE_DIR/$session_id.json"
37
15
  log "INFO" "SessionEnd finalized session_id=$session_id"
@@ -9,49 +9,13 @@ tracing_enabled || exit 0
9
9
  check_requirements || exit 0
10
10
 
11
11
  input="$(cat)"
12
- session_id="$(echo "$input" | jq -r '.session_id // empty')"
13
- [[ -z "$session_id" ]] && session_id="$(uuidgen | tr '[:upper:]' '[:lower:]')"
12
+ result="$(printf '%s' "$input" | python3 "$SCRIPT_DIR/py/cli.py" session-start)"
13
+ IFS=$'\t' read -r session_id trace_id status <<<"$result"
14
+ [[ -z "$session_id" ]] && exit 0
14
15
 
15
- existing_trace_id="$(get_session_state "$session_id" trace_id)"
16
- existing_session_span_id="$(get_session_state "$session_id" session_span_id)"
17
- if [[ -n "$existing_trace_id" && -n "$existing_session_span_id" ]]; then
18
- if [[ -z "$(get_session_state "$session_id" session_start_ns)" ]]; then
19
- set_session_state "$session_id" session_start_ns "$(now_ns)"
20
- fi
21
- if [[ -z "$(get_session_state "$session_id" pending_tool_calls)" ]]; then
22
- set_session_state "$session_id" pending_tool_calls "[]"
23
- fi
24
- if [[ -z "$(get_session_state "$session_id" session_parent_span_id)" ]]; then
25
- set_session_state "$session_id" session_parent_span_id ""
26
- fi
27
- if [[ -z "$(get_session_state "$session_id" session_traceparent_version)" ]]; then
28
- set_session_state "$session_id" session_traceparent_version ""
29
- fi
30
- if [[ -z "$(get_session_state "$session_id" session_trace_flags)" ]]; then
31
- set_session_state "$session_id" session_trace_flags ""
32
- fi
33
- if [[ -z "$(get_session_state "$session_id" trace_context_source)" ]]; then
34
- set_session_state "$session_id" trace_context_source "generated"
35
- fi
36
- log "INFO" "SessionStart ignored existing state session_id=$session_id trace_id=$existing_trace_id"
16
+ if [[ "$status" == "existing" ]]; then
17
+ log "INFO" "SessionStart ignored existing state session_id=$session_id trace_id=$trace_id"
37
18
  exit 0
38
19
  fi
39
20
 
40
- load_initial_trace_context || true
41
- trace_id="${PL_INITIAL_TRACE_ID:-}"
42
- [[ -z "$trace_id" ]] && trace_id="$(generate_trace_id)"
43
- span_id="$(generate_span_id)"
44
- start_ns="$(now_ns)"
45
-
46
- set_session_state "$session_id" trace_id "$trace_id"
47
- set_session_state "$session_id" session_span_id "$span_id"
48
- set_session_state "$session_id" session_parent_span_id "${PL_INITIAL_PARENT_SPAN_ID:-}"
49
- set_session_state "$session_id" session_start_ns "$start_ns"
50
- set_session_state "$session_id" current_turn_start_ns ""
51
- set_session_state "$session_id" pending_tool_calls "[]"
52
- set_session_state "$session_id" session_init_source "session_start_hook"
53
- set_session_state "$session_id" session_traceparent_version "${PL_INITIAL_TRACEPARENT_VERSION:-}"
54
- set_session_state "$session_id" session_trace_flags "${PL_INITIAL_TRACE_FLAGS:-}"
55
- set_session_state "$session_id" trace_context_source "${PL_INITIAL_TRACE_CONTEXT_SOURCE:-generated}"
56
-
57
21
  log "INFO" "SessionStart captured session_id=$session_id trace_id=$trace_id"
@@ -9,115 +9,12 @@ tracing_enabled || exit 0
9
9
  check_requirements || exit 0
10
10
 
11
11
  input="$(cat)"
12
- session_id="$(echo "$input" | jq -r '.session_id // empty')"
13
- transcript_path="$(echo "$input" | jq -r '.transcript_path // empty')"
14
-
15
- if [[ -z "$session_id" && -n "$transcript_path" ]]; then
16
- session_id="$(basename "$transcript_path" .jsonl)"
17
- fi
12
+ result="$(printf '%s' "$input" | python3 "$SCRIPT_DIR/py/cli.py" stop-hook)"
13
+ IFS=$'\t' read -r session_id status <<<"$result"
18
14
  [[ -z "$session_id" ]] && exit 0
19
- spans_file="$(mktemp "${TMPDIR:-/tmp}/pl-stop-spans.XXXXXX")"
20
- cleanup() {
21
- rm -f "$spans_file"
22
- release_session_lock
23
- }
24
- trap cleanup EXIT
25
-
26
- add_span_to_batch() {
27
- local trace="$1"
28
- local span="$2"
29
- local parent="$3"
30
- local span_name="$4"
31
- local span_kind="$5"
32
- local start="$6"
33
- local end="$7"
34
- local attrs="$8"
35
-
36
- local span_json
37
- span_json="$(build_span_json "$trace" "$span" "$parent" "$span_name" "$span_kind" "$start" "$end" "$attrs")" || return 1
38
- printf '%s\n' "$span_json" >>"$spans_file"
39
- }
40
-
41
- acquire_session_lock "$session_id" || exit 0
42
- ensure_session_initialized "$session_id"
43
-
44
- trace_id="$(get_session_state "$session_id" trace_id)"
45
- session_span_id="$(get_session_state "$session_id" session_span_id)"
46
- session_parent_span_id="$(get_session_state "$session_id" session_parent_span_id)"
47
- turn_start_ns="$(get_session_state "$session_id" current_turn_start_ns)"
48
- pending_tool_calls="$(get_session_state "$session_id" pending_tool_calls)"
49
- session_init_source="$(get_session_state "$session_id" session_init_source)"
50
- session_start_ns="$(get_session_state "$session_id" session_start_ns)"
51
-
52
- [[ -z "$trace_id" || -z "$session_span_id" ]] && exit 0
53
- [[ -z "$pending_tool_calls" ]] && pending_tool_calls='[]'
54
- [[ -z "$session_start_ns" ]] && session_start_ns="$(now_ns)"
55
-
56
- [[ -z "$turn_start_ns" ]] && turn_start_ns="$(now_ns)"
57
15
 
58
- # Keep lock scope short: snapshot + clear turn-specific mutable state.
59
- set_session_state "$session_id" current_turn_start_ns ""
60
- set_session_state "$session_id" pending_tool_calls "[]"
61
-
62
- release_session_lock
63
-
64
- parse_transcript_with_retry() {
65
- local attempts=0
66
- local parsed llm_count
67
- while true; do
68
- parsed="$(PL_PENDING_TOOL_CALLS="$pending_tool_calls" python3 "$SCRIPT_DIR/parse_stop_transcript.py" "$transcript_path" "$turn_start_ns" "$session_id")"
69
- llm_count="$(echo "$parsed" | jq -r '.llms | length')"
70
- if [[ "$llm_count" -gt 0 || $attempts -ge 10 ]]; then
71
- echo "$parsed"
72
- return 0
73
- fi
74
- attempts=$((attempts + 1))
75
- sleep 0.2
76
- done
77
- }
78
-
79
- if [[ -z "$transcript_path" || ! -f "$transcript_path" ]]; then
16
+ if [[ "$status" == "missing_transcript" ]]; then
80
17
  log "WARN" "Stop missing transcript session_id=$session_id"
81
- else
82
- parsed="$(parse_transcript_with_retry)"
83
-
84
- turn_start_ns="$(echo "$parsed" | jq -r '.turn.start_ns')"
85
- turn_end_ns="$(echo "$parsed" | jq -r '.turn.end_ns')"
86
-
87
- # Emit (or re-emit) the root session span eagerly so the trace is visible
88
- # in the UI before the session ends. The server upserts on span_id conflict,
89
- # so re-emitting with an updated end time is safe.
90
- if [[ "$session_init_source" == "lazy_init" ]]; then
91
- session_hook_attr="StopFallback"
92
- session_lifecycle_attr="stop_fallback"
93
- else
94
- session_hook_attr="Stop"
95
- session_lifecycle_attr="in_progress"
96
- fi
97
- session_attrs="{\"source\":\"claude-code\",\"hook\":\"$session_hook_attr\",\"node_type\":\"WORKFLOW\",\"session.lifecycle\":\"$session_lifecycle_attr\"}"
98
- add_span_to_batch "$trace_id" "$session_span_id" "$session_parent_span_id" "Claude Code session" "1" "$session_start_ns" "$turn_end_ns" "$session_attrs" || true
99
-
100
- while IFS= read -r tool; do
101
- [[ -z "$tool" ]] && continue
102
- span_id="$(generate_span_id)"
103
- name="$(echo "$tool" | jq -r '.name')"
104
- start_ns="$(echo "$tool" | jq -r '.start_ns')"
105
- end_ns="$(echo "$tool" | jq -r '.end_ns')"
106
- attrs="$(echo "$tool" | jq -c '.attributes')"
107
- add_span_to_batch "$trace_id" "$span_id" "$session_span_id" "$name" "3" "$start_ns" "$end_ns" "$attrs" || true
108
- done < <(echo "$parsed" | jq -c '.tools[]?')
109
-
110
- while IFS= read -r llm; do
111
- [[ -z "$llm" ]] && continue
112
- span_id="$(generate_span_id)"
113
- name="$(echo "$llm" | jq -r '.name')"
114
- start_ns="$(echo "$llm" | jq -r '.start_ns')"
115
- end_ns="$(echo "$llm" | jq -r '.end_ns')"
116
- attrs="$(echo "$llm" | jq -c '.attributes')"
117
- add_span_to_batch "$trace_id" "$span_id" "$session_span_id" "$name" "3" "$start_ns" "$end_ns" "$attrs" || true
118
- done < <(echo "$parsed" | jq -c '.llms[]?')
119
18
  fi
120
19
 
121
- emit_spans_batch_file "$spans_file" || true
122
-
123
20
  log "INFO" "Stop finalized session_id=$session_id"
@@ -9,17 +9,7 @@ tracing_enabled || exit 0
9
9
  check_requirements || exit 0
10
10
 
11
11
  input="$(cat)"
12
- session_id="$(echo "$input" | jq -r '.session_id // empty')"
12
+ session_id="$(printf '%s' "$input" | python3 "$SCRIPT_DIR/py/cli.py" user-prompt-submit)"
13
13
  [[ -z "$session_id" ]] && exit 0
14
14
 
15
- ensure_session_initialized "$session_id"
16
-
17
- trace_id="$(get_session_state "$session_id" trace_id)"
18
- session_span_id="$(get_session_state "$session_id" session_span_id)"
19
- [[ -z "$trace_id" || -z "$session_span_id" ]] && exit 0
20
- start_ns="$(now_ns)"
21
-
22
- set_session_state "$session_id" current_turn_start_ns "$start_ns"
23
- set_session_state "$session_id" pending_tool_calls "[]"
24
-
25
15
  log "INFO" "UserPromptSubmit captured session_id=$session_id"