Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .codespellignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
mor
tok
fpan
dpan
31 changes: 31 additions & 0 deletions .github/workflows/linter.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
# Copyright 2026 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

name: Lint

on:
push:
branches: [main]
pull_request:
branches: [main]

jobs:
pre-commit:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: "3.x"
- uses: pre-commit/action@v3.0.1
45 changes: 45 additions & 0 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
# Copyright 2026 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

exclude: '^(\.github/|\.vscode/|node_modules/).*|CODE_OF_CONDUCT\.md|CHANGELOG\.md'

repos:
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v6.0.0
hooks:
- id: trailing-whitespace
- id: end-of-file-fixer
- id: check-yaml
args: [--unsafe]
- id: check-json
- id: check-added-large-files
- id: detect-private-key
- id: check-shebang-scripts-are-executable
- id: check-executables-have-shebangs
- repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.14.13
hooks:
- id: ruff
args: [--fix, --exit-non-zero-on-fix]
- id: ruff-format
- repo: https://github.com/pre-commit/mirrors-prettier
rev: v4.0.0-alpha.8
hooks:
- id: prettier
files: \.(ts|tsx|js|jsx|json|md|yaml|yml|css|html)$
- repo: https://github.com/codespell-project/codespell
rev: v2.4.1
hooks:
- id: codespell
args: ["--skip", "*.lock,package-lock.json", "-I", ".codespellignore"]
8 changes: 8 additions & 0 deletions .prettierrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"tabWidth": 2,
"useTabs": false,
"singleQuote": false,
"trailingComma": "es5",
"bracketSameLine": false,
"printWidth": 80
}
32 changes: 16 additions & 16 deletions a2a/chat-client/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ type RequestPart =
function createChatMessage(
sender: Sender,
text: string,
props: Partial<ChatMessage> = {},
props: Partial<ChatMessage> = {}
): ChatMessage {
return {
id: crypto.randomUUID(),
Expand All @@ -49,7 +49,7 @@ function createChatMessage(
const initialMessage: ChatMessage = createChatMessage(
Sender.MODEL,
appConfig.defaultMessage,
{ id: "initial" },
{ id: "initial" }
);

/**
Expand All @@ -58,7 +58,7 @@ const initialMessage: ChatMessage = createChatMessage(
*/
function App() {
const [user_email, _setUserEmail] = useState<string | null>(
"foo@example.com",
"foo@example.com"
);
const [messages, setMessages] = useState<ChatMessage[]>([initialMessage]);
const [isLoading, setIsLoading] = useState(false);
Expand Down Expand Up @@ -96,20 +96,20 @@ function App() {
if (!checkout || !checkout.payment || !checkout.payment.handlers) {
const errorMessage = createChatMessage(
Sender.MODEL,
"Sorry, I couldn't retrieve payment methods.",
"Sorry, I couldn't retrieve payment methods."
);
setMessages((prev) => [...prev, errorMessage]);
return;
}

//find the handler with id "example_payment_provider"
const handler = checkout.payment.handlers.find(
(handler: PaymentHandler) => handler.id === "example_payment_provider",
(handler: PaymentHandler) => handler.id === "example_payment_provider"
);
if (!handler) {
const errorMessage = createChatMessage(
Sender.MODEL,
"Sorry, I couldn't find the supported payment handler.",
"Sorry, I couldn't find the supported payment handler."
);
setMessages((prev) => [...prev, errorMessage]);
return;
Expand All @@ -119,7 +119,7 @@ function App() {
const paymentResponse =
await credentialProvider.current.getSupportedPaymentMethods(
user_email,
handler.config,
handler.config
);
const paymentMethods = paymentResponse.payment_method_aliases;

Expand All @@ -131,7 +131,7 @@ function App() {
console.error("Failed to resolve mandate:", error);
const errorMessage = createChatMessage(
Sender.MODEL,
"Sorry, I couldn't retrieve payment methods.",
"Sorry, I couldn't retrieve payment methods."
);
setMessages((prev) => [...prev, errorMessage]);
}
Expand All @@ -145,7 +145,7 @@ function App() {
const userActionMessage = createChatMessage(
Sender.USER,
`User selected payment method: ${selectedMethod}`,
{ isUserAction: true },
{ isUserAction: true }
);
setMessages((prev) => [...prev, userActionMessage]);

Expand All @@ -157,7 +157,7 @@ function App() {
const paymentInstrument =
await credentialProvider.current.getPaymentToken(
user_email,
selectedMethod,
selectedMethod
);

if (!paymentInstrument || !paymentInstrument.credential) {
Expand All @@ -172,7 +172,7 @@ function App() {
console.error("Failed to process payment mandate:", error);
const errorMessage = createChatMessage(
Sender.MODEL,
"Sorry, I couldn't process the payment. Please try again.",
"Sorry, I couldn't process the payment. Please try again."
);
setMessages((prev) => [...prev, errorMessage]);
}
Expand All @@ -183,7 +183,7 @@ function App() {
const userActionMessage = createChatMessage(
Sender.USER,
`User confirmed payment.`,
{ isUserAction: true },
{ isUserAction: true }
);
// Let handleSendMessage manage the loading indicator
setMessages((prev) => [
Expand All @@ -210,7 +210,7 @@ function App() {
console.error("Error confirming payment:", error);
const errorMessage = createChatMessage(
Sender.MODEL,
"Sorry, there was an issue confirming your payment.",
"Sorry, there was an issue confirming your payment."
);
// If handleSendMessage wasn't called, we might need to manually update state
// In this case, we remove the loading indicator that handleSendMessage would have added
Expand All @@ -221,7 +221,7 @@ function App() {

const handleSendMessage = async (
messageContent: string | RequestPart[],
options?: { isUserAction?: boolean; headers?: Record<string, string> },
options?: { isUserAction?: boolean; headers?: Record<string, string> }
) => {
if (isLoading) return;

Expand All @@ -231,7 +231,7 @@ function App() {
? "<User Action>"
: typeof messageContent === "string"
? messageContent
: "Sent complex data",
: "Sent complex data"
);
if (userMessage.text) {
// Only add if there's text
Expand Down Expand Up @@ -366,7 +366,7 @@ function App() {
console.error("Error sending message:", error);
const errorMessage = createChatMessage(
Sender.MODEL,
"Sorry, something went wrong. Please try again.",
"Sorry, something went wrong. Please try again."
);
// Replace the placeholder with the error message
setMessages((prev) => [...prev.slice(0, -1), errorMessage]);
Expand Down
2 changes: 1 addition & 1 deletion a2a/chat-client/components/Checkout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ const CheckoutComponent: React.FC<CheckoutProps> = ({
<p className="text-gray-800 font-medium pl-2">
{formatCurrency(
getItemTotal(lineItem).amount,
checkout.currency,
checkout.currency
)}
</p>
</div>
Expand Down
4 changes: 2 additions & 2 deletions a2a/chat-client/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ export class AppProperties {
description: string,
logoUrl: string,
defaultMessage: string,
titleText: string,
titleText: string
) {
this.name = name;
this.description = description;
Expand All @@ -40,5 +40,5 @@ export const appConfig = new AppProperties(
"Your personal shopping assistant.",
"/images/logo.jpg",
"Hello, I am your Business Agent. How can I help you?",
"Shop with Business Agent",
"Shop with Business Agent"
);
2 changes: 1 addition & 1 deletion a2a/chat-client/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,5 +26,5 @@ const root = ReactDOM.createRoot(rootElement);
root.render(
<React.StrictMode>
<App />
</React.StrictMode>,
</React.StrictMode>
);
10 changes: 5 additions & 5 deletions a2a/chat-client/mocks/credentialProviderProxy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -61,11 +61,11 @@ export class CredentialProviderProxy {
async getSupportedPaymentMethods(
user_email: string,
// biome-ignore lint/suspicious/noExplicitAny: no specific type for config
config: any,
config: any
): Promise<{ payment_method_aliases: PaymentMethod[] }> {
console.log(
`CredentialProviderProxy: Simulating fetch for ${user_email} supported payment methods with config:`,
config,
config
);
// Simulate network latency
await new Promise((resolve) => setTimeout(resolve, 500));
Expand All @@ -80,17 +80,17 @@ export class CredentialProviderProxy {
*/
async getPaymentToken(
user_email: string,
payment_method_id: string,
payment_method_id: string
): Promise<PaymentInstrument | undefined> {
console.log(
`CredentialProviderProxy: Simulating fetch for payment token for user ${user_email} and method ${payment_method_id}`,
`CredentialProviderProxy: Simulating fetch for payment token for user ${user_email} and method ${payment_method_id}`
);
// Simulate network latency
await new Promise((resolve) => setTimeout(resolve, 500));
const randomId = crypto.randomUUID();
const payment_method =
this._getMockPaymentMethods().payment_method_aliases.find(
(method) => method.id === payment_method_id,
(method) => method.id === payment_method_id
);

if (!payment_method) {
Expand Down
6 changes: 3 additions & 3 deletions a2a/docs/05-frontend.md
Original file line number Diff line number Diff line change
Expand Up @@ -179,7 +179,7 @@ class CredentialProviderProxy {
// Returns mock payment methods (wrapped in object)
async getSupportedPaymentMethods(
user_email: string,
config: any,
config: any
): Promise<{ payment_method_aliases: PaymentMethod[] }> {
return {
payment_method_aliases: [
Expand All @@ -206,7 +206,7 @@ class CredentialProviderProxy {
// Converts method to PaymentInstrument with token
async getPaymentToken(
user_email: string,
payment_method_id: string,
payment_method_id: string
): Promise<PaymentInstrument | undefined> {
return {
...payment_method,
Expand Down Expand Up @@ -243,6 +243,6 @@ export const appConfig = new AppProperties(
"Your personal shopping assistant.",
"/images/logo.jpg",
"Hello, I am your Business Agent...",
"Shop with Business Agent",
"Shop with Business Agent"
);
```
Loading
Loading