Skip to content

S20a: lousho.node.id on agent, chat, tool, subagent and flow spans - #862

Open
LinuxDevil wants to merge 1 commit into
mainfrom
fb-s20a-node-id
Open

LinuxDevil wants to merge 1 commit into
mainfrom
fb-s20a-node-id

Conversation

@LinuxDevil

Copy link
Copy Markdown
Owner

What changed

Every span an agent compiled from a v2 spec emits now carries lousho.node.id (SdkAttr.NODE_ID) — the canvas node id of the thing that produced it — so Studio can light up the right node without guessing from names. No executor change: specV2ToAgent wraps host.exporter in a new exporter that stamps the attribute in onSpanStart from an index built from the spec and the compiled tool names, then forwards the same span objects in the same order. Only node ids land on spans — never spec values or secrets.

  • src/execution/semconv.ts: SdkAttr.NODE_ID = 'lousho.node.id' (re-exported from the root; api/index.api.md regenerated).
  • src/spec/v2/nodeIds.ts (browser-safe, pure): specNodeIds(spec) returns the spec's node ids by section — tools (tool:<id ?? builtin ?? registry ?? hosted ?? integration ?? file basename w/o ext ?? "openapi" ?? "hub">, deduped #2, #3), mcp:<key>, skill:<…>, memory:<name>, sub:<name>, handoff:<agent>, guard/perm/hook/trigger/abort:<id>, and flow:<step id> for every step of spec.flow.flow (depth-first through steps, options[].step, step).
  • src/spec/v2/nodeIdExporter.ts: nodeIdIndexOf(spec, toolNamesByRef, subIndexes) builds the span→node index; nodeIdExporter(inner, index) stamps lousho.node.id at start via a rule table (invoke_agent → enclosing agent or <scope>sub:<n> with a new child scope, chat → enclosing agent, execute_tool → tool/MCP/handoff/memory lookup, flow.node … → flow:<id>) and deletes scope entries on end; isNodeIdExporter brands wrapped exporters with a WeakSet so nested compiles never wrap twice.
  • src/spec/v2/specToAgent.ts: when host.exporter is set and not already wrapped, an empty mutable index + wrapper are created, the spec compiles against the wrapped exporter (inline sub-agents share the one scope map), and the index is filled — including each spec-built sub-agent's own index — before createAgent() returns, i.e. before any span can start.

How tested

  • npx tsc --noEmit — clean. npm run typecheck:tests — clean. npm run lint — clean.
  • npx vitest run src/spec/v2 — 424/430 pass; the only 6 failures are templates.test.ts README assertions that fail identically on the base commit (verified via git stash); unrelated to this change.
  • npm run test:coverage -- --minWorkers=1 --maxWorkers=4 — same 6 pre-existing failures, everything else green (7210 pass).
  • npm run fallow — ✓ 0 above threshold, maintainability 89.2. npm run api:check, npm run docs:ratchet (at baseline), npm run docs:llms:check, npm run size-check — all pass.
  • New tests: nodeIds.test.ts (12: one per section + dedupe + flow walk + empty spec) and nodeIdExporter.test.ts (8: unit stamping of hand-built spans incl. sub-agent scoping, unknown-parent root scope, scope cleanup, isNodeIdExporter; integration: real specV2ToAgent runs with a scripted mock model asserting agent, tool:current_date, tool:fetch, sub:research, sub:research/tool:web_fetch).

Acceptance criteria

  • A run of a spec agent with tools [{ builtin: 'current_date' }, { builtin: 'web_fetch', id: 'fetch' }], traced into an in-memory exporter, has: invoke_agent → agent; every chat → agent; execute_tool current_date → tool:current_date; execute_tool web_fetch → tool:fetch.
  • An MCP tool span docs__search gets mcp:docs (fed directly to the exporter; no MCP server needed).
  • remember_notes gets memory:notes; transfer_to_billing gets handoff:billing.
  • A sub-agent run (task tool → sub's invoke_agent with lousho.subagent.name = research) gets sub:research, its chat spans sub:research, and its own tool web_fetch → sub:research/tool:web_fetch.
  • A flow.node span with lousho.flow.node.id = s1 gets flow:s1.
  • specNodeIds dedupes two { builtin: 'http_request' } refs to tool:http_request and tool:http_request#2.
  • Spans without a match have no lousho.node.id; the inner exporter receives the same span objects.

Guardrail check spans (S20b), handoff/approval-wait spans (S20c) and Studio's WS node mapping (D14) are out of scope.

Generated with Devin

…ow spans

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant