Skip to content

feat: A2A-T SDK 1.1.0 upgrade and host-callback engine architecture - #29

Merged
Zhoujie628 merged 49 commits into
project-openan:mainfrom
Zhoujie628:main
Aug 31, 2026
Merged

Zhoujie628 merged 49 commits into
project-openan:mainfrom
Zhoujie628:main

Conversation

@Zhoujie628

Copy link
Copy Markdown
Contributor

Summary

Brings the engine's common track to the published A2A-T SDK 1.1.0 and restructures the engine around an explicit host-callback contract. 49 commits; all carry DCO signoff.

Key changes

  • SDK 1.1.0 from Maven Central (377973d): drops the pinned unreleased local build (1.0.0-0de5a27) and the CI steps that built it; the engine now consumes the published release directly.
  • Protocol traffic observability (f8acfc0): ObservedHttpClient / WireGrpcInterceptor / WireLog observe the wire at the application boundary after SDK serialization, with redaction guarantees covered by dedicated tests.
  • Host-callback architecture (47005e9): replaces the extension-handler plugin machinery (ExtensionRegistry / TaskTHandler / NegotiationTHandler / A2ATContentFacade / TaskDispatcher) with the ControlPoint callback contract — the engine owns DAG scheduling, the A2A envelope, auth, transport, and task waiting; the host owns message content, schemas, templates, and semantic validation (BusinessInput / TaskResult / NegotiationReply / RouteRequest model layer). Transport state and business content are now mapped independently (ProtocolResultAdapter, FailureMapping).
  • Samples aligned with SDK 1.1.0 (c34d8d3): authorization and negotiation flows follow the current typed APIs and the caller-owned NegotiationContext.
  • Docs (ad205f4, e8794f4): business-callback integration contract (en + zh), refreshed SPN integration guidance, and the canonical Negotiation-T extension URI fix.

Earlier commits in this batch

The remaining commits cover the previously-reviewed incremental track: reusable credential profiles, external AgentCard loading, pre-workflow failure isolation, wire-protocol lifecycle hardening, and the corresponding docs/tests.

Test plan

  • mvn clean verify on the full reactor — 268/268 tests green
  • Direct-OMC SPN demo E2E (embedded OMCs, offline LLM provider) — two overlapping city diagnoses, one merge, negotiation rounds, notification closure, clean shutdown
  • Unit suites for the wire-observation layer (redaction included) and the callback boundary
  • No vendor SDK types in common modules; Order adapter remains dev-only and is not part of this PR

…contract

- adopt TemplateUri/StandardTemplates value types on all prompt-generation calls
- rename to validateProposePromptAndDataFilling with explicit NegotiationContext param
- add A2ATContentFacade: full SDK content-layer surface (generation x8, state
  machine x3, validate x4, template queries x4) exposed on A2ATransport
- expose template queries on WorkflowEngineClient (getPrompts/getNegotiationPrompts/getPrompt)
- inject business negotiation param schema via config (engine core stays domain-agnostic)
- negotiation auto-loop: Accept/Reject/Abort terminal rendering via SDK templates,
  round-exhaustion abort flow, rendered follow-up message body (was placeholder)
- fix SSE hang on INPUT_REQUIRED: read propose text+metadata from status message,
  merge status-message metadata into response extraction
- stop re-triggering negotiation on completed replies carrying business results
- constant-ize engine-internal negotiation metadata keys
- refactor autoNegotiate into sub-50-line methods, drop dead code

Signed-off-by: Zhoujie628 <zhoujie628@users.noreply.github.com>
- render Negotiation-T propose via SDK templates (negotiationType/buildProposeContent/
  proposeTemplateUri subclass extension points, three negotiation types)
- send propose as INPUT_REQUIRED status message (standalone agent Message closes
  the SSE stream per a2a-java SDK spec behavior)
- validate-and-fill follow-ups with explicit NegotiationContext from metadata;
  render Accept and attach to the result artifact metadata
- SDK-driven needsNegotiation via validateTaskPromptAndDataFilling (blank slot triggers)
- server-side validate full set: validateAuth/validateNotification with compliance fallback
- NegotiationStrategy: structured Accept payload per SDK scenario data (drop legacy
  in-text context extraction removed from templates)
- A2ATProtocolCases demo reproducing the 11 protocol verification cases + template query case
- negotiation E2E integration test (negotiation auto-loop completes over real SSE)
- fix Negotiation-T extension URI casing in agent cards (NEGOTIATION-T -> Negotiation-T/v1)

Signed-off-by: Zhoujie628 <zhoujie628@users.noreply.github.com>
SpnCrossCityDiagnosisDemo and StartAgentsServer duplicated SpringSpnDemo's flow
(agents startup + Task-T send) without the transport-mode support; unreferenced
except javadoc mentions, zero doc coverage.

Signed-off-by: Zhoujie628 <zhoujie628@users.noreply.github.com>
- API_REFERENCE (zh/en): template queries, negotiation auto-loop behavior
  (trigger, Accept/Reject/Abort, round-exhaustion abort), A2ATContentFacade
  surface, negotiationParamSchema config, internal metadata key conventions,
  full license headers note; error handling updated to the abort flow
- DEVELOPER_GUIDE (zh/en): document the a2at.llm.disabled test switch
- README: negotiation content layer and template query features
- adaptation plan: mark reform items 1-6 as landed, note post-landing SDK
  contract changes (core.model.NegotiationContext, explicit context param,
  metadata-carried negotiation context)
- notification-t-design-analysis: point to current file paths

Signed-off-by: Zhoujie628 <zhoujie628@users.noreply.github.com>
…mo on main

- move EnvResolver/LlmHelper to examples/util (mirrors dev layout)
- move EmbeddedA2AServerTest to examples/server, fix WorkbenchControlPoint
  import in SpnCrossCityE2ETest to the workbench package
- replace the old spring/ demo pair with a direct-transport SpringSpnDemo
  in examples/demo (no vendor gateway coupling)
- introduce WorkbenchClientProperties for orch-url/credentials/ssl-verify/
  a2at-env-path configuration; drop dev-only transport-mode knobs
- remove superseded duplicates in examples/agents (WorkbenchControlPoint,
  WorkbenchOrchestrator, ExtensionPrePositioner, PrePositionedExtensionHandler)
- samples/pom: add a2a-java-sdk-spec-grpc, a2a-t-server and
  protobuf-java-util needed by the generic samples; no vendor SDK
- application.yml: document the public demo certificate caveat

Verified: mvn test -pl samples -am -> 4 tests pass, BUILD SUCCESS
Signed-off-by: Zhoujie628 <zhoujie628@users.noreply.github.com>
Bring over the non-vendor parts of the dev-branch guide updates:
- transport lifecycle clarification (task-level vs workbench-level
  A2ATransport instances to keep Notification-T streams alive)
- downstream AgentCard security field semantics (securitySchemes as
  capability, securityRequirements gating credentials auth)
- AuthProvider/credentials header merge rules and conflict error
- defaultInputModes/defaultOutputModes and skill description fields
  in the workbench AgentCard example
- defaultInputModes note and prompt-compliance guidance refresh

Vendor-specific sections (Eastcom SDK token acquisition, Order-mode
concurrent NE troubleshooting 6.7) stay on dev only.

Signed-off-by: Zhoujie628 <zhoujie628@users.noreply.github.com>
…logic

- delete TransportWorkbenchAgentExecutor: no callers anywhere, only
  Javadoc references (the Spring-managed SpringWorkbenchExecutor is
  the sole workbench executor)
- extract SpnCasePrompts: the ~600-char Task-T private-line complaint
  prompt was hardcoded identically in SpringSpnDemo and three times in
  A2ATProtocolCases; now defined once with well-formed / blank-object /
  unknown-port variants per spec cases 7.1/7.3/7.4
- extract OmcAgentLauncher: the load-agentcard -> resolve-bind-address
  -> start-server boilerplate was duplicated in SpringSpnDemo,
  A2ATProtocolCases and both integration tests
- SpringSpnDemo: only start the HTTP mock gateway (26400) in mock mode;
  order mode routes through the Eastcom Order SDK and its bundled
  protocol simulator (26401), so the 26400 instance sat idle
- update DESIGN-NOTES.md and notification-t-design-analysis.md to the
  current class names

Verified: mvn verify all green (23 samples tests); SpringSpnDemo runs
end-to-end in direct, mock and order+simulator modes; A2ATProtocolCases
case 7.1 completes.

Signed-off-by: Zhoujie628 <zhoujie628@users.noreply.github.com>
The workbench executor lives with the orchestration it drives. This
restores a clean layering: agents/ now contains only the OMC-side
executors (zero cross-package dependencies), workbench/ is
self-contained, and the agents -> workbench / agents -> gateway
dependency edges disappear.

Update the Javadoc reference in SpringWorkbenchApplication, the class
table in the SpringSpnDemo call-chain doc (also fixes several stale
pre-reorg paths) and the DESIGN-NOTES structure diagram.

Verified: mvn test all green (23 samples tests); SpringSpnDemo
direct mode runs end-to-end.

Signed-off-by: Zhoujie628 <zhoujie628@users.noreply.github.com>
…e stateful negotiation loop

Close the gaps found in the SDK-usage audit; the engine now exercises
the SDK's full surface per the official demo patterns instead of
degrading to fallback paths.

fromData dual track (E1):
- WorkflowEngineClient.sendMessageFromData(agent, message, data,
  schema, templateUri): deterministic Task-T rendering via
  generateTaskPromptFromDataWithSchema (no scenario recognition, no
  LLM); travels through the a2at.taskData/taskSchema/taskTemplate
  metadata keys consumed by TaskTHandler
- ExtensionSender.sendExtensionMessageFromData: same track for
  Authorization-T / Notification-T pre-positioning
- A2ATContentFacade exposes generateTask/Auth/NotificationFromData

Stateful negotiation loop (E2) — the auto-loop previously degraded on
every round ('must be a string: negotiationType', 'No negotiation
context', 'Unsupported negotiation type'):
- agent propose now carries the startNegotiation payload's context in
  the negotiation_context metadata key (SDK demo convention)
- NegotiationTHandler reads negotiation_context first, hands the raw
  context map to receiveNegotiation, and validates the full propose
  text (not the short parts placeholder) through
  validateProposePromptAndDataFilling
- context synthesis covers both context shapes (raw map and
  receiveNegotiation result) for follow-up rendering
- requires the SDK client-handler fix (a2a-t-sdk-java
  fix/client-negotiation-handlers) to fully run

Follow-up dual track (E3): 'data:{json}' clarification prefix renders
the Accept deterministically via generateAcceptFromData; reject:/abort:
and plain-text (fromText) paths unchanged

Demo passes original data (D1): SpnCasePrompts now holds the raw
complaint fields + schema; SpringSpnDemo and A2ATProtocolCases send
data through the fromData track, letting the SDK render the Task-T
prompt (577 chars via Task-T/private-line-complaint/v1) instead of
hardcoding the rendered prompt text.

Verified: mvn verify all green (105+5+23 tests); SpringSpnDemo
end-to-end in direct/mock/order modes with the full SDK negotiation
chain logged (propose fromData -> receiveNegotiation ->
validatePropose extracted params -> generateAcceptFromText); protocol
case 7.1 completes.

Signed-off-by: Zhoujie628 <zhoujie628@users.noreply.github.com>
API_REFERENCE (zh/en): sendMessageFromData / sendExtensionMessageFromData
signatures and track-selection guidance, the clarified auto-loop
description (receiveNegotiation -> validate -> render -> continue),
the negotiation_context metadata convention, and the data:/reject:/abort:
clarification prefixes on onNegotiation.

Signed-off-by: Zhoujie628 <zhoujie628@users.noreply.github.com>
…, template fixes

Complete the dual-track rollout to every remaining send path and fix the
template mismatches found in the sweep:

- WorkbenchControlPoint.onTask: southbound workbench->OMC task dispatch
  now goes through sendMessageFromData with city-scoped data (city1 =
  blank slots to trigger negotiation, city2 = complete complaint); the
  hand-built City1/City2 Task-T prompt strings are gone
- WorkflowEngineClient.sendMessageFromData gains the extraMetadata
  overload (workbench passes the empty Negotiation-T key to activate the
  extension header alongside the data track)
- ExtensionPrePositioner: Authorization-T / Notification-T pre-positioning
  now renders via sendExtensionMessageFromData /
  sendNotificationFromData with typed policy/subscription data and
  schemas mirroring the bundled SDK slot schemas
- ExtensionSender.sendNotificationFromData implemented (event callback
  preserved; shared stream-open path extracted)
- Template fixes: Notification-T default is SERVICE_RECOVERY (service
  recovery event), not SUBSCRIBE_INCIDENT (incident subscription) — the
  wrong template was rendering the recovery subscription against the
  incident template; agent-side validate schema aligned to the
  service-recovery slots (drop the non-existent 通知主题 slot)
- TaskTHandler: negotiation-follow-up skip now requires a NON-EMPTY
  Negotiation-T value (an empty value only activates the extension
  header on the first send and must not skip fromData rendering);
  engine-internal a2at.taskData keys are stripped on that path so they
  never reach the wire
- advanceStateMachine: the continueNegotiation context is surfaced as
  the top-level negotiation_context key so the agent-side parser finds
  a full stateful map on the follow-up
- NegotiationBaseAgentExecutor.parseNegotiationContext: same fallback
  chain as the engine side (negotiationContext -> negotiation_context,
  both raw and receive-result shapes)

Verified: mvn verify all green; direct and order demos end-to-end with
ZERO degradation warnings — the full chain runs the SDK path: fromData
Task-T render (city1+city2), stateful propose, receiveNegotiation,
validatePropose extracted [id, round, maxRounds, 接入端口名称, 投诉分类],
SDK Accept render on both sides.

Signed-off-by: Zhoujie628 <zhoujie628@users.noreply.github.com>
…machine

Per upstream review (a2a-t-sdk-java PR #138): negotiation/{types, store,
runtime, handler} is deprecated demo-era code slated for removal; the
current architecture is content/ + generation/ + resources/ + validation/
exposed via NegotiationContentService. The engine now uses only current
APIs — when the SDK deletes the deprecated packages, nothing here breaks.

Migration (the content layer is stateless by design — session identity
and round tracking stay with the caller, SDK guide §1.10):

- A2ATContentFacade: drop the startNegotiation / receiveNegotiation /
  continueNegotiation pass-throughs; add contextPayload/contextFromMap —
  the engine's serialization of the session context ({id, round,
  maxRounds}) for the negotiation_context metadata key (with legacy
  state-machine-format tolerance in the parser)
- NegotiationTHandler.afterReceive: no receiveNegotiation call — the
  INPUT_REQUIRED task state plus the propose text in metadata already
  signal the pending negotiation; the engine answers directly and
  validates the propose through the current
  validateProposePromptAndDataFilling
- DefaultWorkflowEngineClient.advanceStateMachine: replace the SDK state
  machine with engine-held rounds (core.model.NegotiationContext.
  nextRound(); terminal on reject/abort), re-serialized into
  negotiation_context; drop the NegotiationPayloadMapper /
  types.model dependencies entirely
- samples NegotiationBaseAgentExecutor: agent-owned session context
  (UUID + round 1 + DEFAULT_MAX_ROUNDS) serialized into the propose
  metadata instead of server.startNegotiation; negotiationType()
  extension point replaced by proposeTemplateUri() (the template URI
  encodes the type — no deprecated enum needed)
- NegotiationUtils.negotiationResponseMetadata takes the context map
  directly (no startNegotiation payload extraction)

Verified against clean trunk SDK (823f76a, PR #136 included): mvn verify
all green (105+5+23); direct/order demos end-to-end with the full
negotiation chain on current APIs — propose context (id/round/maxRounds)
→ validatePropose extracted params → engine Context advanced round 1->2
→ SDK accept rendering; A2ATProtocolCases case 7.1 completes.

Signed-off-by: Zhoujie628 <zhoujie628@users.noreply.github.com>
Signed-off-by: Zhoujie628 <zhoujie628@users.noreply.github.com>
Signed-off-by: Zhoujie628 <zhoujie628@users.noreply.github.com>
Signed-off-by: Zhoujie628 <zhoujie628@users.noreply.github.com>
Signed-off-by: Zhoujie628 <zhoujie628@users.noreply.github.com>
Signed-off-by: Zhoujie628 <zhoujie628@users.noreply.github.com>
Signed-off-by: Zhoujie628 <zhoujie628@users.noreply.github.com>
Signed-off-by: Zhoujie628 <zhoujie628@users.noreply.github.com>
Signed-off-by: Zhoujie628 <zhoujie628@users.noreply.github.com>
Signed-off-by: Zhoujie628 <zhoujie628@users.noreply.github.com>
Signed-off-by: Zhoujie628 <zhoujie628@users.noreply.github.com>
Signed-off-by: Zhoujie628 <zhoujie628@users.noreply.github.com>
Signed-off-by: Zhoujie628 <zhoujie628@users.noreply.github.com>
Signed-off-by: Zhoujie628 <zhoujie628@users.noreply.github.com>
Signed-off-by: Zhoujie628 <zhoujie628@users.noreply.github.com>
Signed-off-by: Zhoujie628 <zhoujie628@users.noreply.github.com>
Signed-off-by: Zhoujie628 <zhoujie628@users.noreply.github.com>
Signed-off-by: Zhoujie628 <zhoujie628@users.noreply.github.com>
Signed-off-by: Zhoujie628 <zhoujie628@users.noreply.github.com>
Signed-off-by: Zhoujie628 <zhoujie628@users.noreply.github.com>
Pin the A2A-T SDK to revision 841c575 and migrate the engine onto its
current APIs:

- NegotiationContext now carries a mandatory performative; context
  parsing rejects malformed or performative-less wire contexts
- Replace the clarification control-string contract (data:/reject:/
  abort: prefixes) with the typed NegotiationDecision model routed to
  the exact SDK fromText/fromData method; abortData validates the
  termination reason before invoking the SDK
- Introduce NegotiationRequest as the typed business view of an
  INPUT_REQUIRED negotiation, carrying session/round/performative and
  the propose template kind
- Introduce TaskDispatcher and TaskSubmission so business callbacks
  dispatch natural-language or structured Task-T input without
  manipulating protocol metadata
- Update samples and bilingual docs to the typed APIs

Signed-off-by: Zhoujie628 <zhoujie628@users.noreply.github.com>
Apply the generic parts of the dev-side integration guide updates to
main: independent Authorization-T/Notification-T operation sections,
the Negotiation-T/v1 extension URI, openNotificationFromData
subscription lifecycle, and the extension sender API table row.

The Eastcom Order SDK 1.1.18 ByteBuf troubleshooting section stays
dev-only.

Signed-off-by: Zhoujie628 <zhoujie628@users.noreply.github.com>
Strip vendor-specific content from the main branch:

- drop the vendor-SDK concurrent NE timeout troubleshooting section
  from the English integration guide (it stays in the dev-only docs)
- drop the vendor transport-mode and instruction-platform env block
  from .env.example
- reduce the dual-transport verification wording in the SDK dependency
  doc and the business-flow doc to the direct-transport path

Main carries only the generic direct-transport engine; vendor adapter
material lives on dev.

Signed-off-by: Zhoujie628 <zhoujie628@users.noreply.github.com>
Signed-off-by: Zhoujie628 <zhoujie628@users.noreply.github.com>
Signed-off-by: Zhoujie628 <zhoujie628@users.noreply.github.com>
Signed-off-by: Zhoujie628 <zhoujie628@users.noreply.github.com>
Signed-off-by: Zhoujie628 <zhoujie628@users.noreply.github.com>
Signed-off-by: Zhoujie628 <zhoujie628@users.noreply.github.com>
Signed-off-by: Zhoujie628 <zhoujie628@users.noreply.github.com>
Signed-off-by: Zhoujie628 <zhoujie628@users.noreply.github.com>
Signed-off-by: Zhoujie628 <zhoujie628@users.noreply.github.com>
Signed-off-by: Zhoujie628 <zhoujie628@users.noreply.github.com>
Signed-off-by: Zhoujie628 <zhoujie628@users.noreply.github.com>
Signed-off-by: Zhoujie628 <zhoujie628@users.noreply.github.com>
…lbacks

Move A2A-T content generation and validation into sample business callbacks; preserve structured upstream outputs and independent extension lifecycles. Cover default single-city negotiation and current SDK flows.

Signed-off-by: Zhoujie628 <zhoujie628@users.noreply.github.com>
Signed-off-by: Zhoujie628 <zhoujie628@users.noreply.github.com>
Signed-off-by: Zhoujie628 <zhoujie628@users.noreply.github.com>
Signed-off-by: Zhoujie628 <zhoujie628@users.noreply.github.com>
@Zhoujie628
Zhoujie628 requested a review from a team as a code owner August 31, 2026 02:04
@Zhoujie628
Zhoujie628 merged commit 92b4488 into project-openan:main Aug 31, 2026
3 checks passed
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