@amaster.ai/dsh-langfuse 0.1.35 → 0.1.37

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/README.md CHANGED
@@ -7,7 +7,7 @@ Langfuse observability for [DeepSeek Harness (dsh)](https://github.com/deepseek-
7
7
  - one **generation** per LLM call (`llm/stream` waterfall), plus a nested `llm-request` span carrying the verbatim loop-built request
8
8
  - one **tool observation** per tool call (`tools/pre-execute` through `tools/result`)
9
9
  - one **span** per `hook/invoked` / `hook/result` pair, with hook point, decision, exit code, and duration (stderr summary follows `captureContent`)
10
- - one **trace** per session turn (`session/event`) — in the v5 SDK the trace IS its root span, ended (and thereby exported) at `turn/end`
10
+ - one **trace** per session turn with observed activity (`session/event`) — the root is created on the first message or observed operation, retains the original turn start time, and ends at `turn/end`; completed turns without activity export nothing, while errors and cancellation remain visible
11
11
  - subagent child sessions nested under the parent's tree (`session/created` header link + `subagent/start` / `subagent/end`)
12
12
  - structured reasoning in generation output, optional image display, inherited user/tags, and final turn severity
13
13
  - optional W3C parent context supplied per session, without adopting the host's ambient OTEL context
package/lib/index.js CHANGED
@@ -13,7 +13,7 @@
13
13
  * shared identity).
14
14
  * - `hook/invoked` / `hook/result` session records → one **span** per hook run.
15
15
  * - `session/event` emit → one Langfuse **trace** per agent turn
16
- * (`turn/start` opens a root span — in the v5 SDK the root observation IS
16
+ * (`turn/start` records the start; actual activity opens the root — in v5 it IS
17
17
  * the trace — the turn's first `user/message` sets the input, `turn/end`
18
18
  * stamps the end reason and ends the root, which is what exports it).
19
19
  * - Native A2A inbox tasks own a trace spanning admission through task termination;
@@ -590,8 +590,13 @@ export function apply(ctx, config) {
590
590
  }
591
591
  });
592
592
  function openTrace(sessionId, metadata, startTime) {
593
- const context = sessionId ? traceContexts.get(sessionId) : undefined;
594
- if (sessionId)
593
+ const pending = sessionId ? pendingTurns.get(sessionId) : undefined;
594
+ const context = pending
595
+ ? pending.context
596
+ : sessionId
597
+ ? traceContexts.get(sessionId)
598
+ : undefined;
599
+ if (sessionId && !pending)
595
600
  traceContexts.delete(sessionId);
596
601
  return reporter.openTrace({ name: config.traceName, sessionId, metadata, context, startTime });
597
602
  }
@@ -1120,17 +1125,20 @@ export function apply(ctx, config) {
1120
1125
  if (stale)
1121
1126
  endTurn(stale);
1122
1127
  inboxInputs.delete(sessionId);
1123
- if (nativeInboxes.has(sessionId)) {
1124
- sessions.delete(sessionId);
1125
- pendingTurns.set(sessionId, { turn: event.data.turn, startTime: new Date(event.time) });
1126
- break;
1127
- }
1128
- const trace = openTrace(sessionId, { turn: event.data.turn });
1129
- if (trace)
1130
- sessions.set(sessionId, { trace, hasInput: false, turn: event.data.turn });
1128
+ sessions.delete(sessionId);
1129
+ pendingTurns.set(sessionId, {
1130
+ turn: event.data.turn,
1131
+ startTime: new Date(event.time),
1132
+ context: nativeInboxes.has(sessionId) ? undefined : traceContexts.get(sessionId),
1133
+ });
1134
+ // Ordinary turns consume request context at start, even if they remain empty.
1135
+ if (!nativeInboxes.has(sessionId))
1136
+ traceContexts.delete(sessionId);
1131
1137
  break;
1132
1138
  }
1133
1139
  case 'user/message': {
1140
+ if (pendingTurns.has(sessionId))
1141
+ traceFor(sessionId);
1134
1142
  const state = sessions.get(sessionId);
1135
1143
  if (state && !state.hasInput && config.captureContent) {
1136
1144
  setMessageInput(state.trace, event.data.content);
@@ -1139,6 +1147,8 @@ export function apply(ctx, config) {
1139
1147
  break;
1140
1148
  }
1141
1149
  case 'assistant/message': {
1150
+ if (pendingTurns.has(sessionId))
1151
+ traceFor(sessionId);
1142
1152
  const state = sessions.get(sessionId);
1143
1153
  if (state && config.captureContent) {
1144
1154
  state.lastAssistantText = messageText(event.data.message) ?? state.lastAssistantText;
@@ -1146,8 +1156,10 @@ export function apply(ctx, config) {
1146
1156
  break;
1147
1157
  }
1148
1158
  case 'turn/end': {
1149
- if (pendingTurns.has(sessionId))
1159
+ if (pendingTurns.has(sessionId) &&
1160
+ (nativeInboxes.has(sessionId) || event.data.reason.kind !== 'completed'))
1150
1161
  traceFor(sessionId);
1162
+ pendingTurns.delete(sessionId);
1151
1163
  const state = sessions.get(sessionId);
1152
1164
  if (state) {
1153
1165
  sessions.delete(sessionId);
@@ -13,7 +13,7 @@
13
13
  * shared identity).
14
14
  * - `hook/invoked` / `hook/result` session records → one **span** per hook run.
15
15
  * - `session/event` emit → one Langfuse **trace** per agent turn
16
- * (`turn/start` opens a root span — in the v5 SDK the root observation IS
16
+ * (`turn/start` records the start; actual activity opens the root — in v5 it IS
17
17
  * the trace — the turn's first `user/message` sets the input, `turn/end`
18
18
  * stamps the end reason and ends the root, which is what exports it).
19
19
  * - Native A2A inbox tasks own a trace spanning admission through task termination;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@amaster.ai/dsh-langfuse",
3
- "version": "0.1.35",
3
+ "version": "0.1.37",
4
4
  "description": "Langfuse observability plugin for DeepSeek Harness: one generation per LLM call with a nested llm-request span, one span per tool call, one trace per session turn, subagent child sessions nested under the parent's tree.",
5
5
  "type": "module",
6
6
  "main": "lib/index.js",