You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
v4: wording-only corrections from the v3 review — (1) C7 prose no longer claims the future registry will retroactively mark mid-June orphans; replaced with an honest "future rows only; historical rows remain pair-key-only unless a separate backfill is designed" framing. (2) v3 revision-history bullet rewritten to stop calling adk-2.0 design: pause registry read-after-write strategy #206 a C7 blocker — the dependency graph already correctly says NOT blocked by #206; the bullet now matches that, framing adk-2.0 design: pause registry read-after-write strategy #206 as the deferred home for orphan semantics rather than a blocker. No contract changes.
v3: applied review corrections — (1) C7 narrowed for the mid-June bar: emit TOOL_PAUSED and non-HITL TOOL_COMPLETED with the pairable keys (function_call_id, pause_kind); the pause registry's read-after-write visibility semantics (pause_orphan, settling delay vs in-process cache vs in-session reconstruction) defer to adk-2.0 design: pause registry read-after-write strategy #206, which is now named as the deferred home for orphan semantics (C7 itself is not blocked by adk-2.0 design: pause registry read-after-write strategy #206 — see the dependency graph). The customer's mid-June cutover needs row-pair join keys, not orphan semantics. (2) C8 attribute path realigned to flat-with-prefix to match adk-2.0 producer: capture or explicitly defer actions.route / render_ui_widgets / rewind_before_invocation_id #203 and the entire envelope convention in Tracking: ADK 2.0 — workflow- and team-aware tracing across producer and consumer #190 (attributes.adk.branch, attributes.adk.scope, attributes.adk.app_name, attributes.adk.source_event_id, attributes.adk.node, attributes.adk.pause_kind — all flat). C8 now reads attributes.adk.route, attributes.adk.render_ui_widgets, attributes.adk.rewind_before_invocation_id (was: nested under attributes.adk.actions.*). Acceptance Overhaul README, add documentation indexes, and fix CI issues #4 and the test plan updated to match. (3) Reviewer's HIGH on AGENT_STATE_CHECKPOINT blank event.id was rejected with verification: Event.model_post_init (events/event.py:271-275) auto-assigns id = Event.new_id() for any Event constructed without one, including the _create_agent_state_event path (agents/base_agent.py:213-218). Empirically verified: Event(invocation_id='inv-1', author='agentA', branch='b', actions=EventActions()).id is a 36-char UUID immediately after construction, before the runner hands the Event to on_event_callback. No stabilization rule needed; C6 rows have real source_event_ids.
v2: applied review corrections — (1) C7 HITL routing fix: long-running-path TOOL_COMPLETED is non-HITL-only and always carries pause_kind = 'tool'; HITL function responses stay on the existing HITL_*_COMPLETED stream (verified against bigquery_agent_analytics_plugin.py:2944,3029), not TOOL_COMPLETED. TOOL_PAUSED may still carry HITL pause_kinds. (2) Envelope contract split: A1/A2 (schema_version, app_name) stamp on every ADK-enriched row regardless of origin; A3/C1/C2/C3 (source_event_id, node, branch, scope) only on rows with a real originating Event or typed telemetry context — no fabricated identity for callback-only rows. (3) Producer null-safety reframed: tests now cover default-empty NodeInfo.path = "" (events/event.py:45), absent branch/isolation_scope staying JSON null, and explicit no-fabricated-node behavior — historical pre-2.0 table rows are the consumer's problem (Tracking: ADK 2.0 — workflow- and team-aware tracing across producer and consumer #190/adk-2.0 consumer: register six new event types across all SDK type surfaces #211), not this issue's. (4) AGENT_TRANSFER.from_agent source pinned: from_agent = event.author, to_agent = event.actions.transfer_to_agent (verified event_actions.py:75 carries the target only). (5) Typo: deferred OTel row now correctly references A3 (source_event_id), not C3. (6) Precision edits: C5 compaction timestamps must preserve fractional float-epoch-second precision in the producer (view conversion deferred); C1 gets an explicit NodeInfo.path == "" fixture; acceptance Revise README for clarity and updated link #3 SQL key spelled with JSON paths (JSON_VALUE(attributes, '$.adk.app_name'), JSON_VALUE(attributes, '$.adk.function_call_id')).
Spawned from the customer-needs comment on #190: #190 (comment)
Scope: producer side only (the BQ AA Plugin in google/adk-python, src/google/adk/plugins/bigquery_agent_analytics_plugin.py). Consumer SDK views in #190 are not on this critical path — once the producer writes the rows, the customer can query them directly via SQL while the typed views land separately.
Why this issue exists
#190 is the long-form ADK 2.0 tracking issue (28 sub-issues, producer + consumer). The customer pinned a comment listing the specific fields they need before they take ADK 2.0 to production mid-June:
event.long_running_tool_ids
event.node_info
actions.compaction
actions.transfer_to_agent
actions.end_of_agent / agent_state
actions.route / UI / rewind
Workflow boundaries
This issue is a minimum, scoped subset of #190 that maps 1:1 to that list, ordered so the customer can ship without waiting for the full plan (workflow-node-boundary derivation, OTel correlation, optional adapters, consumer views, pause-orphan registry semantics — all deferred).
Group A — envelope every enriched row carries (blocks everything else)
A1 and A2 stamp on every ADK-enriched row regardless of whether the row originates from an Event. A3 only stamps on rows with a real originating Event (or a future typed telemetry context that carries an equivalent identity). The producer must not synthesize a source_event_id for callback-only rows; consumers can detect Event-originating rows via WHERE JSON_VALUE(attributes, '$.adk.source_event_id') IS NOT NULL. Note: any Event constructed without an id (e.g. _create_agent_state_event in agents/base_agent.py:213-218) is auto-stabilized by Event.model_post_init (events/event.py:271-275), which sets self.id = Event.new_id() before the plugin sees it — no additional producer-side stabilization needed.
A1. attributes.adk.schema_version — constant on every ADK-enriched row. Single source of truth for "this row was produced by an ADK 2.0-aware plugin build".
A2. attributes.adk.app_name — from InvocationContext.app_name, on every ADK-enriched row. Required for the pause registry composite key and for the general cross-event identity rule.
A3. attributes.adk.source_event_id — only on rows that originate from an Event (or a typed telemetry context carrying an equivalent identity, per B0). Leave null on rows produced by callbacks that don't receive an Event from the framework. Reliable join key against ADK OTel associated_event_ids. Never fabricate.
Group B — prerequisite plumbing (blocks A3 + C1/C2/C3 for non-on_event_callback paths)
B0. Thread the source Event (or a typed telemetry context) into EventData so _log_event can enrich rows that don't originate from on_event_callback. Today's EventData (bigquery_agent_analytics_plugin.py:1956) carries span/latency/model fields only and no source Event. Covered in detail by adk-2.0 producer: thread source Event / node-info into EventData for non-on_event callbacks #194 — this issue depends on adk-2.0 producer: thread source Event / node-info into EventData for non-on_event callbacks #194 landing first or in parallel. Deliverable: a per-callback coverage matrix that explicitly enumerates, for every callback the plugin implements, whether A3/C1/C2/C3 enrichment is feasible (real Event reachable), best-effort, or null (no Event from framework). This issue's acceptance criteria only require enrichment where the matrix says it's feasible.
Group C — customer-prioritized event/action capture
Each maps to one bullet in the customer's comment. Acceptance for each = a fixture turn that exercises the path produces the expected BigQuery row(s) on rows the B0 matrix marks as Event-originating.
C1. attributes.adk.node = {path, run_id, parent_path} on Event-originating rows. parent_path is everything before the final /<segment>@<run_id> when path contains a /; otherwise JSON null. NodeInfo.path defaults to "" (events/event.py:45) — the producer must emit the empty string verbatim (or a documented null form) and must not synthesize a fake workflow node or parent path from it. → maps to adk-2.0 producer: enrich rows with attributes.adk.node (path, run_id, parent_path) #196. Customer ask: event.node_info.
C4. AGENT_TRANSFER from actions.transfer_to_agent. EventActions.transfer_to_agent stores the target agent only (event_actions.py:75), so payload derivation is pinned: to_agent = event.actions.transfer_to_agent; from_agent = event.author on Event-originating transfer rows. If a non-Event path ever needs to emit a transfer row (none today), it must supply an equivalent typed source or leave from_agent null with a warning log — no fabricated authors. → maps to adk-2.0 producer: emit AGENT_TRANSFER from actions.transfer_to_agent #200. Customer ask: actions.transfer_to_agent.
long-running-path TOOL_COMPLETED is non-HITL only and always carries attributes.adk.pause_kind = 'tool'. HITL completions remain on HITL_CREDENTIAL_REQUEST_COMPLETED / HITL_CONFIRMATION_REQUEST_COMPLETED / HITL_INPUT_REQUEST_COMPLETED.
Required attributes on both TOOL_PAUSED and (non-HITL) long-running TOOL_COMPLETED: attributes.adk.pause_kind, attributes.adk.function_call_id. These are the pair keys the customer joins on in SQL.
pause_orphan and the registry's read-after-write visibility semantics (in-process cache vs settling-time delay vs in-session reconstruction) are out of scope for this issue — they defer to adk-2.0 design: pause registry read-after-write strategy #206, the dedicated design blocker. Customer SQL during the mid-June window will join TOOL_PAUSED ↔ TOOL_COMPLETED directly and accept the small set of unpaired rows that the eventual-visibility window can produce. The registry strategy lands later for future rows; historical rows emitted by this mid-June cut remain pair-key-only unless a separate backfill is explicitly designed.
Customer ask: event.long_running_tool_ids.
D1. Delete on_state_change_callback (deprecated stub at bigquery_agent_analytics_plugin.py:3131, never called by ADK 2.0). Tiny but removes a misleading surface area for the customer's team when they read the plugin code.
Explicitly deferred (post-cutover)
Each carries a one-line reason so the customer's team understands why their ask is not blocked:
Deferred
Reason
WORKFLOW_NODE_STARTING/COMPLETED event types
Design-blocked in #190 / #207 (OTel-span vs event-observation). Workflow boundaries are partially observable today via attributes.adk.node (C1) on Event-originating rows, which is enough for the customer's first production query needs. The dedicated boundary events come after #207's decision lands.
Blocks the orphan-flag contract, not the row-pair join keys. The customer can compute long-running tool durations from direct TOOL_PAUSED ↔ TOOL_COMPLETED SQL joins during the mid-June window; orphan correctness lands when #206's strategy is chosen and the registry is added.
Optional bqaa_adk.py SDK adapter
Not customer-blocking.
Consumer SDK view registration (#211, views.py) for the new event types
Not customer-blocking — they can read base-table JSON until the typed views land. Tracked in #190.
Historical pre-2.0 table-row null-safety
Concerns rows already in the table that were written by an older producer. Belongs to consumer view work (#190 / #211), not this producer-only issue.
Dependency graph
B0 (#194) ───┬──► A3, C1, C2, C3, C4–C7
│ (only on B0-feasible callbacks)
A1, A2 ─┴──► every ADK-enriched row (no Event dep)
C4 AGENT_TRANSFER ◄── actions.transfer_to_agent
(from_agent=event.author)
C5 EVENT_COMPACTION ◄── actions.compaction
(preserve float precision)
C6 AGENT_STATE_CHECKPOINT ◄── actions.agent_state | end_of_agent
C7 TOOL_PAUSED + non-HITL TOOL_COMPLETED enrich
◄── event.long_running_tool_ids
NOT blocked by #206 (orphan semantics deferred)
C8 attributes.adk.{route, render_ui_widgets, rewind_before_invocation_id}
◄── actions.route / widgets / rewind
(covered by #203, same flat-with-prefix shape)
D1 — cleanup, no deps
Acceptance criteria
A representative ADK 2.0 invocation that includes: agent transfer + event compaction + agent-state checkpoint (both {agent_state: null, end_of_agent: true} and {agent_state: {...}, end_of_agent: false} shapes) + a long-running pause that spans invocations + a actions.route or rewind_before_invocation_id action → produces BigQuery rows where, from the customer's SQL only:
Every ADK-enriched row exposes attributes.adk.schema_version and attributes.adk.app_name. Event-originating rows additionally exposeattributes.adk.source_event_id, attributes.adk.node, attributes.adk.branch, attributes.adk.scope per the B0 coverage matrix; callback-only rows leave these JSON null without synthesis.
AGENT_TRANSFER, EVENT_COMPACTION, AGENT_STATE_CHECKPOINT rows are joinable by (JSON_VALUE(attributes, '$.adk.app_name'), user_id, session_id, invocation_id).
The customer can pair TOOL_PAUSED ↔ long-running TOOL_COMPLETED on (JSON_VALUE(attributes, '$.adk.app_name'), user_id, session_id, JSON_VALUE(attributes, '$.adk.function_call_id')) filtered to JSON_VALUE(attributes, '$.adk.pause_kind') = 'tool' on both rows, and compute long-running tool duration without the SDK's typed view and without the pause registry. Orphan rows are tolerated for the mid-June window; orphan correctness lands with adk-2.0 design: pause registry read-after-write strategy #206. HITL completion durations are computed separately from the existing HITL_*_REQUEST → HITL_*_REQUEST_COMPLETED stream.
Producer-side tests stay green; new fixtures cover each emit path.
on_state_change_callback is gone from the public surface.
Test plan (producer-only)
Per-emit-path fixture for C4–C7 + C8.
Envelope smoke (A1, A2) asserted on every row produced by an existing fixture.
Envelope smoke (A3) asserted only on rows the B0 coverage matrix marks Event-originating; non-Event-originating callback rows asserted to leave A3 / C1 / C2 / C3 JSON null (not sentinel-stringed).
Empty-path fixture: an Event with NodeInfo.path == "" must emit attributes.adk.node.path = "" (or a documented null form) and must not synthesize a workflow node or parent_path.
AGENT_STATE_CHECKPOINT id-stabilization smoke: an _create_agent_state_event-shaped Event (constructed without id=) must arrive at the plugin with a non-empty id, and the resulting BigQuery row must carry that id in attributes.adk.source_event_id. (This is a regression guard for the Pydantic model_post_init auto-assign path; it does not require new producer logic.)
C7 HITL non-routing assertion: a long-running pause whose function_call name is adk_request_confirmation (etc.) produces TOOL_PAUSED with pause_kind = 'hitl_confirmation', and the matching function response produces a HITL_CONFIRMATION_REQUEST_COMPLETED row — not a TOOL_COMPLETED row.
C7 pair-key assertion (no orphan logic): a non-HITL long-running pause + completion produces a TOOL_PAUSED and a TOOL_COMPLETED row with matching function_call_id and pause_kind = 'tool' on both. No pause_orphan assertion — that field's correctness defers to adk-2.0 design: pause registry read-after-write strategy #206.
C5 fractional-timestamp fixture: an actions.compaction with sub-second start_timestamp / end_timestamp must round-trip through the producer's JSON serialization with fractional precision intact.
If staffed seriously, A1–A3 + C1–C5 + C7 (pair-key) should be reachable by mid-June. C6 (inline) and C8 are small additions to the same PRs. Workflow node boundaries and pause-orphan semantics explicitly stay deferred.
Why this issue exists
#190 is the long-form ADK 2.0 tracking issue (28 sub-issues, producer + consumer). The customer pinned a comment listing the specific fields they need before they take ADK 2.0 to production mid-June:
event.long_running_tool_idsevent.node_infoactions.compactionactions.transfer_to_agentactions.end_of_agent/agent_stateactions.route/ UI /rewindThis issue is a minimum, scoped subset of #190 that maps 1:1 to that list, ordered so the customer can ship without waiting for the full plan (workflow-node-boundary derivation, OTel correlation, optional adapters, consumer views, pause-orphan registry semantics — all deferred).
Producer state today
Already emitted (no work):
INVOCATION_STARTING/COMPLETED,AGENT_STARTING/RESPONSE/COMPLETED,USER_MESSAGE_RECEIVED,LLM_REQUEST/RESPONSE/ERROR,TOOL_STARTING/COMPLETED/ERROR,STATE_DELTA,HITL_CREDENTIAL_REQUEST/HITL_CREDENTIAL_REQUEST_COMPLETED,HITL_CONFIRMATION_REQUEST/HITL_CONFIRMATION_REQUEST_COMPLETED,HITL_INPUT_REQUEST/HITL_INPUT_REQUEST_COMPLETED.Missing for the customer ask: every item below.
Minimum plan
Group A — envelope every enriched row carries (blocks everything else)
A1 and A2 stamp on every ADK-enriched row regardless of whether the row originates from an
Event. A3 only stamps on rows with a real originatingEvent(or a future typed telemetry context that carries an equivalent identity). The producer must not synthesize asource_event_idfor callback-only rows; consumers can detect Event-originating rows viaWHERE JSON_VALUE(attributes, '$.adk.source_event_id') IS NOT NULL. Note: any Event constructed without anid(e.g._create_agent_state_eventinagents/base_agent.py:213-218) is auto-stabilized byEvent.model_post_init(events/event.py:271-275), which setsself.id = Event.new_id()before the plugin sees it — no additional producer-side stabilization needed.attributes.adk.schema_version— constant on every ADK-enriched row. Single source of truth for "this row was produced by an ADK 2.0-aware plugin build".attributes.adk.app_name— fromInvocationContext.app_name, on every ADK-enriched row. Required for the pause registry composite key and for the general cross-event identity rule.attributes.adk.source_event_id— only on rows that originate from anEvent(or a typed telemetry context carrying an equivalent identity, per B0). Leave null on rows produced by callbacks that don't receive an Event from the framework. Reliable join key against ADK OTelassociated_event_ids. Never fabricate.Group B — prerequisite plumbing (blocks A3 + C1/C2/C3 for non-
on_event_callbackpaths)Event(or a typed telemetry context) intoEventDataso_log_eventcan enrich rows that don't originate fromon_event_callback. Today'sEventData(bigquery_agent_analytics_plugin.py:1956) carries span/latency/model fields only and no sourceEvent. Covered in detail by adk-2.0 producer: thread source Event / node-info into EventData for non-on_event callbacks #194 — this issue depends on adk-2.0 producer: thread source Event / node-info into EventData for non-on_event callbacks #194 landing first or in parallel. Deliverable: a per-callback coverage matrix that explicitly enumerates, for every callback the plugin implements, whether A3/C1/C2/C3 enrichment is feasible (real Event reachable), best-effort, or null (no Event from framework). This issue's acceptance criteria only require enrichment where the matrix says it's feasible.Group C — customer-prioritized event/action capture
Each maps to one bullet in the customer's comment. Acceptance for each = a fixture turn that exercises the path produces the expected BigQuery row(s) on rows the B0 matrix marks as Event-originating.
attributes.adk.node = {path, run_id, parent_path}on Event-originating rows.parent_pathis everything before the final/<segment>@<run_id>whenpathcontains a/; otherwise JSONnull.NodeInfo.pathdefaults to""(events/event.py:45) — the producer must emit the empty string verbatim (or a documented null form) and must not synthesize a fake workflow node or parent path from it. → maps to adk-2.0 producer: enrich rows with attributes.adk.node (path, run_id, parent_path) #196. Customer ask:event.node_info.attributes.adk.branchon Event-originating rows. Absent branch stays JSON null, never a sentinel like""or"default". → maps to adk-2.0 producer: enrich originating-Event rows with attributes.adk.branch #197. Customer ask: implicit in the node-info neighborhood.attributes.adk.scope = null | {id, kind}with the v15 derivation rule (Tracking: ADK 2.0 — workflow- and team-aware tracing across producer and consumer #190 v5 fixed the misclassification bug): (1)None→null; (2)name@<run_id>/<parent>/<name>@<run_id>→node_run; (3) any other non-empty string →function_call; (4) empty/non-string →unknownwith a warning. Absentisolation_scopestaysnull, never a sentinel. → maps to adk-2.0 producer: emit attributes.adk.scope with v15 derivation rule #198. Customer ask: implicit — lets the customer separate node-loop fan-out from function-call fan-out.AGENT_TRANSFERfromactions.transfer_to_agent.EventActions.transfer_to_agentstores the target agent only (event_actions.py:75), so payload derivation is pinned:to_agent = event.actions.transfer_to_agent;from_agent = event.authoron Event-originating transfer rows. If a non-Event path ever needs to emit a transfer row (none today), it must supply an equivalent typed source or leavefrom_agentnull with a warning log — no fabricated authors. → maps to adk-2.0 producer: emit AGENT_TRANSFER from actions.transfer_to_agent #200. Customer ask:actions.transfer_to_agent.EVENT_COMPACTIONfromactions.compaction({start_timestamp, end_timestamp, compacted_content}).start_timestampandend_timestampare ADK float epoch seconds (events/event_actions.pyEventCompactionfields); the producer must preserve fractional precision in the emitted JSON (consumer view conversion is deferred per Tracking: ADK 2.0 — workflow- and team-aware tracing across producer and consumer #190). → maps to adk-2.0 producer: emit EVENT_COMPACTION from actions.compaction #201. Customer ask:actions.compaction.AGENT_STATE_CHECKPOINTwhen eitheractions.agent_state is not Noneoractions.end_of_agent is True. Allow{agent_state: null, end_of_agent: true}payloads. Minimum: inline payload only. GCS offload for oversized state is a separate design item (Tracking: ADK 2.0 — workflow- and team-aware tracing across producer and consumer #190's open design checkbox) — file as a follow-up; don't gate the cutover on it. → maps to adk-2.0 producer: emit AGENT_STATE_CHECKPOINT from actions.agent_state OR end_of_agent #202. Customer ask:actions.end_of_agent/agent_state.TOOL_PAUSED(+ paired long-runningTOOL_COMPLETEDenrichment) fromevent.long_running_tool_ids. Mid-June minimum bar: emit pairable rows only. HITL routing is unchanged — HITL function responses continue routing toHITL_*_COMPLETED(bigquery_agent_analytics_plugin.py:2944,3029), notTOOL_COMPLETED. Therefore:TOOL_PAUSEDmay carry anypause_kind ∈ {tool, hitl_credential, hitl_confirmation, hitl_input}derived per adk-2.0 producer: emit TOOL_PAUSED / TOOL_COMPLETED with pause_kind + function_call_id (long-running event path) #199's id→name lookup (_HITL_EVENT_MAP[part.function_call.name], not against the id value).TOOL_COMPLETEDis non-HITL only and always carriesattributes.adk.pause_kind = 'tool'. HITL completions remain onHITL_CREDENTIAL_REQUEST_COMPLETED/HITL_CONFIRMATION_REQUEST_COMPLETED/HITL_INPUT_REQUEST_COMPLETED.TOOL_PAUSEDand (non-HITL) long-runningTOOL_COMPLETED:attributes.adk.pause_kind,attributes.adk.function_call_id. These are the pair keys the customer joins on in SQL.pause_orphanand the registry's read-after-write visibility semantics (in-process cache vs settling-time delay vs in-session reconstruction) are out of scope for this issue — they defer to adk-2.0 design: pause registry read-after-write strategy #206, the dedicated design blocker. Customer SQL during the mid-June window will joinTOOL_PAUSED↔TOOL_COMPLETEDdirectly and accept the small set of unpaired rows that the eventual-visibility window can produce. The registry strategy lands later for future rows; historical rows emitted by this mid-June cut remain pair-key-only unless a separate backfill is explicitly designed.Customer ask:
event.long_running_tool_ids.actions.route,actions.render_ui_widgets,actions.rewind_before_invocation_id— stamp underattributes.adk.route,attributes.adk.render_ui_widgets,attributes.adk.rewind_before_invocation_id(flat-with-prefix, matching adk-2.0 producer: capture or explicitly defer actions.route / render_ui_widgets / rewind_before_invocation_id #203's contract and the rest of theattributes.adk.*envelope in Tracking: ADK 2.0 — workflow- and team-aware tracing across producer and consumer #190 —branch,scope,node,app_name,source_event_id,pause_kind, etc. are all flat) on the originating-Event row. No new event types for the mid-June bar — emitting them as attributes lets the customer filter/group without forcing a downstream consumer update. → covered by adk-2.0 producer: capture or explicitly defer actions.route / render_ui_widgets / rewind_before_invocation_id #203. Customer ask:actions.route/ UI /rewind.Group D — cleanup
on_state_change_callback(deprecated stub atbigquery_agent_analytics_plugin.py:3131, never called by ADK 2.0). Tiny but removes a misleading surface area for the customer's team when they read the plugin code.Explicitly deferred (post-cutover)
Each carries a one-line reason so the customer's team understands why their ask is not blocked:
WORKFLOW_NODE_STARTING/COMPLETEDevent typesattributes.adk.node(C1) on Event-originating rows, which is enough for the customer's first production query needs. The dedicated boundary events come after #207's decision lands.attributes.adk.otel_span_id(#205)source_event_id↔ ADK's span-sideassociated_event_idsin the meantime.AGENT_STATE_CHECKPOINTpause_orphansemantics + read-after-write visibility (#206)TOOL_PAUSED↔TOOL_COMPLETEDSQL joins during the mid-June window; orphan correctness lands when #206's strategy is chosen and the registry is added.bqaa_adk.pySDK adapter#211,views.py) for the new event typesDependency graph
Acceptance criteria
A representative ADK 2.0 invocation that includes: agent transfer + event compaction + agent-state checkpoint (both
{agent_state: null, end_of_agent: true}and{agent_state: {...}, end_of_agent: false}shapes) + a long-running pause that spans invocations + aactions.routeorrewind_before_invocation_idaction → produces BigQuery rows where, from the customer's SQL only:attributes.adk.schema_versionandattributes.adk.app_name. Event-originating rows additionally exposeattributes.adk.source_event_id,attributes.adk.node,attributes.adk.branch,attributes.adk.scopeper the B0 coverage matrix; callback-only rows leave these JSON null without synthesis.AGENT_TRANSFER,EVENT_COMPACTION,AGENT_STATE_CHECKPOINTrows are joinable by(JSON_VALUE(attributes, '$.adk.app_name'), user_id, session_id, invocation_id).TOOL_PAUSED↔ long-runningTOOL_COMPLETEDon(JSON_VALUE(attributes, '$.adk.app_name'), user_id, session_id, JSON_VALUE(attributes, '$.adk.function_call_id'))filtered toJSON_VALUE(attributes, '$.adk.pause_kind') = 'tool'on both rows, and compute long-running tool duration without the SDK's typed view and without the pause registry. Orphan rows are tolerated for the mid-June window; orphan correctness lands with adk-2.0 design: pause registry read-after-write strategy #206. HITL completion durations are computed separately from the existingHITL_*_REQUEST→HITL_*_REQUEST_COMPLETEDstream.actions.route/render_ui_widgets/rewind_before_invocation_idare surfaced asattributes.adk.route/attributes.adk.render_ui_widgets/attributes.adk.rewind_before_invocation_id(flat-with-prefix, matching adk-2.0 producer: capture or explicitly defer actions.route / render_ui_widgets / rewind_before_invocation_id #203) on the originating Event row.on_state_change_callbackis gone from the public surface.Test plan (producer-only)
NodeInfo.path == ""must emitattributes.adk.node.path = ""(or a documented null form) and must not synthesize a workflow node orparent_path._create_agent_state_event-shaped Event (constructed withoutid=) must arrive at the plugin with a non-emptyid, and the resulting BigQuery row must carry that id inattributes.adk.source_event_id. (This is a regression guard for the Pydanticmodel_post_initauto-assign path; it does not require new producer logic.)node/branch/scopeenrichment fixtures matching the shape-coverage list in Tracking: ADK 2.0 — workflow- and team-aware tracing across producer and consumer #190 v5 (node-run scope, function-call scope, unscoped, model-provided FC IDs).adk_request_confirmation(etc.) producesTOOL_PAUSEDwithpause_kind = 'hitl_confirmation', and the matching function response produces aHITL_CONFIRMATION_REQUEST_COMPLETEDrow — not aTOOL_COMPLETEDrow.TOOL_PAUSEDand aTOOL_COMPLETEDrow with matchingfunction_call_idandpause_kind = 'tool'on both. Nopause_orphanassertion — that field's correctness defers to adk-2.0 design: pause registry read-after-write strategy #206.actions.compactionwith sub-secondstart_timestamp/end_timestampmust round-trip through the producer's JSON serialization with fractional precision intact.Effort + ordering recommendation
Roughly in order of "what unlocks the next thing":
If staffed seriously, A1–A3 + C1–C5 + C7 (pair-key) should be reachable by mid-June. C6 (inline) and C8 are small additions to the same PRs. Workflow node boundaries and pause-orphan semantics explicitly stay deferred.
References
TOOL_PAUSED/TOOL_COMPLETEDemit (C7). adk-2.0 producer: minimum plan to unblock mid-June customer cutover #293 covers only the pair-key subset of adk-2.0 producer: emit TOOL_PAUSED / TOOL_COMPLETED with pause_kind + function_call_id (long-running event path) #199 —TOOL_PAUSEDwithpause_kind, non-HITL long-runningTOOL_COMPLETEDwith matchingfunction_call_id+pause_kind = 'tool'. The pause registry,pause_orphanflag, and read-after-write visibility semantics from adk-2.0 producer: emit TOOL_PAUSED / TOOL_COMPLETED with pause_kind + function_call_id (long-running event path) #199 are deferred to adk-2.0 design: pause registry read-after-write strategy #206 for the mid-June window.AGENT_TRANSFER(C4),EVENT_COMPACTION(C5),AGENT_STATE_CHECKPOINT(C6) emit sub-issues that this minimum plan coversactions.route/render_ui_widgets/rewind_before_invocation_idcapture (C8's home)