@aws/nx-plugin 1.0.0-rc.73 → 1.0.0-rc.75
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/LICENSE-THIRD-PARTY +495 -1188
- package/README.md +7 -89
- package/migrations.json +11 -1
- package/package.json +1 -1
- package/src/internal/test-matrix/generator.js +15 -0
- package/src/internal/test-matrix/generator.js.map +1 -1
- package/src/license/dependency-check/collectors/python-collector.js +9 -3
- package/src/license/dependency-check/collectors/python-collector.js.map +1 -1
- package/src/migrations/latest/py-agent-session-id-middleware-extraction/metadata.json +3 -0
- package/src/migrations/latest/py-agent-session-id-middleware-extraction/migration.d.ts +2 -0
- package/src/migrations/latest/py-agent-session-id-middleware-extraction/migration.js +118 -0
- package/src/migrations/latest/py-agent-session-id-middleware-extraction/migration.js.map +1 -0
- package/src/migrations/latest/ts-agent-session-id-middleware-extraction/metadata.json +3 -0
- package/src/migrations/latest/ts-agent-session-id-middleware-extraction/migration.d.ts +2 -0
- package/src/migrations/latest/ts-agent-session-id-middleware-extraction/migration.js +124 -0
- package/src/migrations/latest/ts-agent-session-id-middleware-extraction/migration.js.map +1 -0
- package/src/py/agent/__snapshots__/generator.constructs.spec.ts.snap +3 -18
- package/src/py/agent/__snapshots__/generator.frameworks.spec.ts.snap +2 -13
- package/src/py/agent/files/langchain/a2a/main.py.template +4 -15
- package/src/py/agent/files/langchain/ag-ui/main.py.template +2 -13
- package/src/py/agent/files/langchain/common/middleware/__init__.py.template +0 -0
- package/src/py/agent/files/langchain/common/middleware/session_id_middleware.py.template +17 -0
- package/src/py/agent/files/langchain/http/main.py.template +2 -14
- package/src/py/agent/files/strands/a2a/main.py.template +4 -15
- package/src/py/agent/files/strands/ag-ui/main.py.template +2 -13
- package/src/py/agent/files/strands/common/middleware/__init__.py.template +0 -0
- package/src/py/agent/files/strands/common/middleware/session_id_middleware.py.template +17 -0
- package/src/py/agent/files/strands/http/main.py.template +2 -17
- package/src/sdk/agentcore-harness.d.ts +6 -0
- package/src/sdk/agentcore-harness.js +7 -0
- package/src/sdk/agentcore-harness.js.map +1 -0
- package/src/sdk/smithy.d.ts +6 -0
- package/src/sdk/smithy.js +7 -0
- package/src/sdk/smithy.js.map +1 -0
- package/src/ts/agent/files/a2a/index.ts.template +4 -11
- package/src/ts/agent/files/ag-ui/index.ts.template +2 -12
- package/src/ts/agent/files/common-express/middleware/session-id-middleware.ts.template +16 -0
- package/src/ts/agent/generator.js +8 -0
- package/src/ts/agent/generator.js.map +1 -1
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
|
3
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
4
|
+
*/ import { readFileSync } from "node:fs";
|
|
5
|
+
import { join } from "node:path";
|
|
6
|
+
import { getProjects, joinPathFragments } from "@nx/devkit";
|
|
7
|
+
import { TS_AGENT_GENERATOR_INFO } from "../../../ts/agent/generator.js";
|
|
8
|
+
import { addDestructuredImport, applyGritQL, captureGritQLVariable, matchGritQL } from "../../../utils/ast.js";
|
|
9
|
+
import { formatFilesInSubtree } from "../../../utils/format.js";
|
|
10
|
+
/**
|
|
11
|
+
* Extract the inline session-id Express middleware - previously duplicated
|
|
12
|
+
* verbatim in every ts#agent AG-UI/A2A `index.ts` - into a shared
|
|
13
|
+
* `middleware/session-id-middleware.ts` module sitting beside `index.ts`,
|
|
14
|
+
* mirroring how `agent.ts`/`session.ts` are already shared siblings.
|
|
15
|
+
*
|
|
16
|
+
* HTTP is untouched: its one procedure is a tRPC subscription served over
|
|
17
|
+
* WebSocket, which bypasses Express entirely, so it never had this middleware
|
|
18
|
+
* to begin with (see `enterSessionContext` in its `router.ts` instead).
|
|
19
|
+
*
|
|
20
|
+
* AG-UI and A2A wire the same logic up differently - AG-UI binds it to a
|
|
21
|
+
* named `sessionIdMiddleware` const before use, while A2A inlines it directly
|
|
22
|
+
* at the `app.use` call site - so each gets its own old-shape pattern below,
|
|
23
|
+
* even though both resolve to the same shared middleware content.
|
|
24
|
+
*
|
|
25
|
+
* Guardrails:
|
|
26
|
+
* - Pattern-match before writing: skip files that have diverged from the
|
|
27
|
+
* generated shape and report them via `nextSteps`, rather than clobbering
|
|
28
|
+
* the user's changes.
|
|
29
|
+
* - Idempotent: re-running is a no-op once migrated.
|
|
30
|
+
*/ const EXPRESS_IMPORT_OLD = "import express, { type Request, type Response, type NextFunction } from 'express';";
|
|
31
|
+
const EXPRESS_IMPORT_NEW = "import express from 'express';";
|
|
32
|
+
const RANDOM_UUID_IMPORT = "import { randomUUID } from 'node:crypto';";
|
|
33
|
+
const SESSION_ID_HEADER_LINE = "const SESSION_ID_HEADER = 'x-amzn-bedrock-agentcore-runtime-session-id';";
|
|
34
|
+
// The leading comment sits directly above both shapes below. GritQL can only
|
|
35
|
+
// match it as its own comment node - folding it into the same backtick
|
|
36
|
+
// snippet as the statement that follows fails to parse as one construct - so
|
|
37
|
+
// it's matched/deleted as a separate step from the statement/expression.
|
|
38
|
+
const MIDDLEWARE_LEADING_COMMENT = '// Bind the inbound session (or a fresh UUID) for downstream MCP / A2A calls.';
|
|
39
|
+
// AG-UI binds the middleware to a named const, used later via `app.use(sessionIdMiddleware)`.
|
|
40
|
+
const AGUI_MIDDLEWARE_DEFINITION_OLD = `const sessionIdMiddleware = (req: Request, _res: Response, next: NextFunction) => {
|
|
41
|
+
const header = req.headers[SESSION_ID_HEADER];
|
|
42
|
+
const sessionId = (Array.isArray(header) ? header[0] : header) ?? randomUUID();
|
|
43
|
+
runWithSessionId(sessionId, () => next());
|
|
44
|
+
};`;
|
|
45
|
+
// A2A inlines the same logic directly at the `app.use` call site instead.
|
|
46
|
+
const A2A_MIDDLEWARE_USE_OLD = `app.use((req: Request, _res: Response, next: NextFunction) => {
|
|
47
|
+
const header = req.headers[SESSION_ID_HEADER];
|
|
48
|
+
const sessionId = (Array.isArray(header) ? header[0] : header) ?? randomUUID();
|
|
49
|
+
runWithSessionId(sessionId, () => next());
|
|
50
|
+
});`;
|
|
51
|
+
const A2A_MIDDLEWARE_USE_NEW = 'app.use(sessionIdMiddleware);';
|
|
52
|
+
// Captures the agent-connection package specifier so the new middleware
|
|
53
|
+
// module targets the same one. Generic over `$names` since a connection
|
|
54
|
+
// generator may have merged its own import into this same statement.
|
|
55
|
+
const RUNWITHSESSIONID_IMPORT_CAPTURE = "`import { $names } from '$mod';` where { $names <: contains `runWithSessionId` }";
|
|
56
|
+
// Removes `runWithSessionId` from the named import, preserving any other
|
|
57
|
+
// merged specifiers (or the whole statement if it was the only one).
|
|
58
|
+
// Decomposed via `import_clause(name=named_imports($imports))` since a naive
|
|
59
|
+
// `$rest`-based rewrite silently fails once other specifiers are involved.
|
|
60
|
+
const REMOVE_RUNWITHSESSIONID_IMPORT_PATTERN = `\`import $clause from '$mod';\` as $import where {
|
|
61
|
+
$clause <: import_clause(name=named_imports($imports)),
|
|
62
|
+
$imports <: contains \`runWithSessionId\`,
|
|
63
|
+
if ($imports <: [\`runWithSessionId\`]) { $import => . } else { $imports <: some import_specifier(name=\`runWithSessionId\`) => . }
|
|
64
|
+
}`;
|
|
65
|
+
// Both protocols' `common-express/` dir vends byte-identical middleware
|
|
66
|
+
// content; read as the single source of truth rather than hand-duplicating
|
|
67
|
+
// it here.
|
|
68
|
+
const SESSION_ID_MIDDLEWARE_TEMPLATE = readFileSync(join(import.meta.dirname, '../../../ts/agent/files/common-express/middleware/session-id-middleware.ts.template'), 'utf-8');
|
|
69
|
+
const sessionIdMiddlewareContent = (agentConnectionImport)=>SESSION_ID_MIDDLEWARE_TEMPLATE.replace('<%- agentConnectionImport %>', agentConnectionImport);
|
|
70
|
+
const findAgentComponents = (components)=>(components ?? []).filter((component)=>component.generator === TS_AGENT_GENERATOR_INFO.id && (component.protocol === 'ag-ui' || component.protocol === 'a2a'));
|
|
71
|
+
const migrateIndex = async (tree, indexPath, protocol, nextSteps)=>{
|
|
72
|
+
if (!tree.exists(indexPath)) return;
|
|
73
|
+
const contents = tree.read(indexPath, 'utf-8') ?? '';
|
|
74
|
+
if (!contents.includes(SESSION_ID_HEADER_LINE)) return;
|
|
75
|
+
const middlewarePattern = protocol === 'ag-ui' ? AGUI_MIDDLEWARE_DEFINITION_OLD : A2A_MIDDLEWARE_USE_OLD;
|
|
76
|
+
const diverged = ()=>{
|
|
77
|
+
nextSteps.push(`${indexPath}: diverged from the generated ts#agent ${protocol} shape - left untouched. Manually move the inline session-id middleware into a sibling \`middleware/session-id-middleware.ts\` module and import \`sessionIdMiddleware\` from there (see the ts#agent generator's template).`);
|
|
78
|
+
};
|
|
79
|
+
const ready = await matchGritQL(tree, indexPath, `\`${EXPRESS_IMPORT_OLD}\``) && await matchGritQL(tree, indexPath, `\`${RANDOM_UUID_IMPORT}\``) && await matchGritQL(tree, indexPath, `\`${SESSION_ID_HEADER_LINE}\``) && await matchGritQL(tree, indexPath, `\`${MIDDLEWARE_LEADING_COMMENT}\``) && await matchGritQL(tree, indexPath, `\`${middlewarePattern}\``) && await matchGritQL(tree, indexPath, RUNWITHSESSIONID_IMPORT_CAPTURE);
|
|
80
|
+
if (!ready) {
|
|
81
|
+
diverged();
|
|
82
|
+
return;
|
|
83
|
+
}
|
|
84
|
+
const mod = await captureGritQLVariable(tree, indexPath, RUNWITHSESSIONID_IMPORT_CAPTURE, 'mod');
|
|
85
|
+
if (!mod) {
|
|
86
|
+
diverged();
|
|
87
|
+
return;
|
|
88
|
+
}
|
|
89
|
+
const dir = indexPath.split('/').slice(0, -1).join('/');
|
|
90
|
+
const middlewarePath = joinPathFragments(dir, 'middleware', 'session-id-middleware.ts');
|
|
91
|
+
if (!tree.exists(middlewarePath)) {
|
|
92
|
+
tree.write(middlewarePath, sessionIdMiddlewareContent(mod));
|
|
93
|
+
}
|
|
94
|
+
await applyGritQL(tree, indexPath, `\`${EXPRESS_IMPORT_OLD}\` => \`${EXPRESS_IMPORT_NEW}\``);
|
|
95
|
+
await applyGritQL(tree, indexPath, `\`${RANDOM_UUID_IMPORT}\` => .`);
|
|
96
|
+
await applyGritQL(tree, indexPath, `\`${SESSION_ID_HEADER_LINE}\` => .`);
|
|
97
|
+
await applyGritQL(tree, indexPath, REMOVE_RUNWITHSESSIONID_IMPORT_PATTERN);
|
|
98
|
+
await applyGritQL(tree, indexPath, `\`${MIDDLEWARE_LEADING_COMMENT}\` => .`);
|
|
99
|
+
if (protocol === 'ag-ui') {
|
|
100
|
+
await applyGritQL(tree, indexPath, `\`${AGUI_MIDDLEWARE_DEFINITION_OLD}\` => .`);
|
|
101
|
+
} else {
|
|
102
|
+
await applyGritQL(tree, indexPath, `\`${A2A_MIDDLEWARE_USE_OLD}\` => \`${A2A_MIDDLEWARE_USE_NEW}\``);
|
|
103
|
+
}
|
|
104
|
+
await addDestructuredImport(tree, indexPath, [
|
|
105
|
+
'sessionIdMiddleware'
|
|
106
|
+
], './middleware/session-id-middleware.js');
|
|
107
|
+
};
|
|
108
|
+
export default async function migration(tree) {
|
|
109
|
+
const nextSteps = [];
|
|
110
|
+
for (const project of getProjects(tree).values()){
|
|
111
|
+
const components = findAgentComponents(project.metadata?.components);
|
|
112
|
+
for (const component of components){
|
|
113
|
+
if (!component.path) continue;
|
|
114
|
+
const indexPath = joinPathFragments(project.root, component.path, 'index.ts');
|
|
115
|
+
await migrateIndex(tree, indexPath, component.protocol, nextSteps);
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
await formatFilesInSubtree(tree);
|
|
119
|
+
return {
|
|
120
|
+
nextSteps
|
|
121
|
+
};
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
//# sourceMappingURL=migration.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../../../../../packages/nx-plugin/src/migrations/latest/ts-agent-session-id-middleware-extraction/migration.ts"],"sourcesContent":["/**\n * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.\n * SPDX-License-Identifier: Apache-2.0\n */\nimport { readFileSync } from 'node:fs';\nimport { join } from 'node:path';\nimport {\n getProjects,\n joinPathFragments,\n type MigrationReturnObject,\n type Tree,\n} from '@nx/devkit';\nimport { TS_AGENT_GENERATOR_INFO } from '../../../ts/agent/generator';\nimport {\n addDestructuredImport,\n applyGritQL,\n captureGritQLVariable,\n matchGritQL,\n} from '../../../utils/ast';\nimport { formatFilesInSubtree } from '../../../utils/format';\nimport type { ComponentMetadata } from '../../../utils/nx';\n\n/**\n * Extract the inline session-id Express middleware - previously duplicated\n * verbatim in every ts#agent AG-UI/A2A `index.ts` - into a shared\n * `middleware/session-id-middleware.ts` module sitting beside `index.ts`,\n * mirroring how `agent.ts`/`session.ts` are already shared siblings.\n *\n * HTTP is untouched: its one procedure is a tRPC subscription served over\n * WebSocket, which bypasses Express entirely, so it never had this middleware\n * to begin with (see `enterSessionContext` in its `router.ts` instead).\n *\n * AG-UI and A2A wire the same logic up differently - AG-UI binds it to a\n * named `sessionIdMiddleware` const before use, while A2A inlines it directly\n * at the `app.use` call site - so each gets its own old-shape pattern below,\n * even though both resolve to the same shared middleware content.\n *\n * Guardrails:\n * - Pattern-match before writing: skip files that have diverged from the\n * generated shape and report them via `nextSteps`, rather than clobbering\n * the user's changes.\n * - Idempotent: re-running is a no-op once migrated.\n */\n\nconst EXPRESS_IMPORT_OLD =\n \"import express, { type Request, type Response, type NextFunction } from 'express';\";\nconst EXPRESS_IMPORT_NEW = \"import express from 'express';\";\n\nconst RANDOM_UUID_IMPORT = \"import { randomUUID } from 'node:crypto';\";\n\nconst SESSION_ID_HEADER_LINE =\n \"const SESSION_ID_HEADER = 'x-amzn-bedrock-agentcore-runtime-session-id';\";\n\n// The leading comment sits directly above both shapes below. GritQL can only\n// match it as its own comment node - folding it into the same backtick\n// snippet as the statement that follows fails to parse as one construct - so\n// it's matched/deleted as a separate step from the statement/expression.\nconst MIDDLEWARE_LEADING_COMMENT =\n '// Bind the inbound session (or a fresh UUID) for downstream MCP / A2A calls.';\n\n// AG-UI binds the middleware to a named const, used later via `app.use(sessionIdMiddleware)`.\nconst AGUI_MIDDLEWARE_DEFINITION_OLD = `const sessionIdMiddleware = (req: Request, _res: Response, next: NextFunction) => {\n const header = req.headers[SESSION_ID_HEADER];\n const sessionId = (Array.isArray(header) ? header[0] : header) ?? randomUUID();\n runWithSessionId(sessionId, () => next());\n};`;\n\n// A2A inlines the same logic directly at the `app.use` call site instead.\nconst A2A_MIDDLEWARE_USE_OLD = `app.use((req: Request, _res: Response, next: NextFunction) => {\n const header = req.headers[SESSION_ID_HEADER];\n const sessionId = (Array.isArray(header) ? header[0] : header) ?? randomUUID();\n runWithSessionId(sessionId, () => next());\n });`;\nconst A2A_MIDDLEWARE_USE_NEW = 'app.use(sessionIdMiddleware);';\n\n// Captures the agent-connection package specifier so the new middleware\n// module targets the same one. Generic over `$names` since a connection\n// generator may have merged its own import into this same statement.\nconst RUNWITHSESSIONID_IMPORT_CAPTURE =\n \"`import { $names } from '$mod';` where { $names <: contains `runWithSessionId` }\";\n\n// Removes `runWithSessionId` from the named import, preserving any other\n// merged specifiers (or the whole statement if it was the only one).\n// Decomposed via `import_clause(name=named_imports($imports))` since a naive\n// `$rest`-based rewrite silently fails once other specifiers are involved.\nconst REMOVE_RUNWITHSESSIONID_IMPORT_PATTERN = `\\`import $clause from '$mod';\\` as $import where {\n $clause <: import_clause(name=named_imports($imports)),\n $imports <: contains \\`runWithSessionId\\`,\n if ($imports <: [\\`runWithSessionId\\`]) { $import => . } else { $imports <: some import_specifier(name=\\`runWithSessionId\\`) => . }\n}`;\n\n// Both protocols' `common-express/` dir vends byte-identical middleware\n// content; read as the single source of truth rather than hand-duplicating\n// it here.\nconst SESSION_ID_MIDDLEWARE_TEMPLATE = readFileSync(\n join(\n import.meta.dirname,\n '../../../ts/agent/files/common-express/middleware/session-id-middleware.ts.template',\n ),\n 'utf-8',\n);\n\nconst sessionIdMiddlewareContent = (agentConnectionImport: string): string =>\n SESSION_ID_MIDDLEWARE_TEMPLATE.replace(\n '<%- agentConnectionImport %>',\n agentConnectionImport,\n );\n\nconst findAgentComponents = (\n components: ComponentMetadata[] | undefined,\n): ComponentMetadata[] =>\n (components ?? []).filter(\n (component) =>\n component.generator === TS_AGENT_GENERATOR_INFO.id &&\n (component.protocol === 'ag-ui' || component.protocol === 'a2a'),\n );\n\nconst migrateIndex = async (\n tree: Tree,\n indexPath: string,\n protocol: 'ag-ui' | 'a2a',\n nextSteps: string[],\n): Promise<void> => {\n if (!tree.exists(indexPath)) return;\n\n const contents = tree.read(indexPath, 'utf-8') ?? '';\n if (!contents.includes(SESSION_ID_HEADER_LINE)) return;\n\n const middlewarePattern =\n protocol === 'ag-ui'\n ? AGUI_MIDDLEWARE_DEFINITION_OLD\n : A2A_MIDDLEWARE_USE_OLD;\n\n const diverged = () => {\n nextSteps.push(\n `${indexPath}: diverged from the generated ts#agent ${protocol} shape - left untouched. Manually move the inline session-id middleware into a sibling \\`middleware/session-id-middleware.ts\\` module and import \\`sessionIdMiddleware\\` from there (see the ts#agent generator's template).`,\n );\n };\n\n const ready =\n (await matchGritQL(tree, indexPath, `\\`${EXPRESS_IMPORT_OLD}\\``)) &&\n (await matchGritQL(tree, indexPath, `\\`${RANDOM_UUID_IMPORT}\\``)) &&\n (await matchGritQL(tree, indexPath, `\\`${SESSION_ID_HEADER_LINE}\\``)) &&\n (await matchGritQL(tree, indexPath, `\\`${MIDDLEWARE_LEADING_COMMENT}\\``)) &&\n (await matchGritQL(tree, indexPath, `\\`${middlewarePattern}\\``)) &&\n (await matchGritQL(tree, indexPath, RUNWITHSESSIONID_IMPORT_CAPTURE));\n\n if (!ready) {\n diverged();\n return;\n }\n\n const mod = await captureGritQLVariable(\n tree,\n indexPath,\n RUNWITHSESSIONID_IMPORT_CAPTURE,\n 'mod',\n );\n if (!mod) {\n diverged();\n return;\n }\n\n const dir = indexPath.split('/').slice(0, -1).join('/');\n const middlewarePath = joinPathFragments(\n dir,\n 'middleware',\n 'session-id-middleware.ts',\n );\n\n if (!tree.exists(middlewarePath)) {\n tree.write(middlewarePath, sessionIdMiddlewareContent(mod));\n }\n\n await applyGritQL(\n tree,\n indexPath,\n `\\`${EXPRESS_IMPORT_OLD}\\` => \\`${EXPRESS_IMPORT_NEW}\\``,\n );\n await applyGritQL(tree, indexPath, `\\`${RANDOM_UUID_IMPORT}\\` => .`);\n await applyGritQL(tree, indexPath, `\\`${SESSION_ID_HEADER_LINE}\\` => .`);\n await applyGritQL(tree, indexPath, REMOVE_RUNWITHSESSIONID_IMPORT_PATTERN);\n await applyGritQL(tree, indexPath, `\\`${MIDDLEWARE_LEADING_COMMENT}\\` => .`);\n\n if (protocol === 'ag-ui') {\n await applyGritQL(\n tree,\n indexPath,\n `\\`${AGUI_MIDDLEWARE_DEFINITION_OLD}\\` => .`,\n );\n } else {\n await applyGritQL(\n tree,\n indexPath,\n `\\`${A2A_MIDDLEWARE_USE_OLD}\\` => \\`${A2A_MIDDLEWARE_USE_NEW}\\``,\n );\n }\n\n await addDestructuredImport(\n tree,\n indexPath,\n ['sessionIdMiddleware'],\n './middleware/session-id-middleware.js',\n );\n};\n\nexport default async function migration(\n tree: Tree,\n): Promise<MigrationReturnObject> {\n const nextSteps: string[] = [];\n\n for (const project of getProjects(tree).values()) {\n const components = findAgentComponents(\n (project.metadata as { components?: ComponentMetadata[] })?.components,\n );\n\n for (const component of components) {\n if (!component.path) continue;\n\n const indexPath = joinPathFragments(\n project.root,\n component.path,\n 'index.ts',\n );\n\n await migrateIndex(\n tree,\n indexPath,\n component.protocol as 'ag-ui' | 'a2a',\n nextSteps,\n );\n }\n }\n\n await formatFilesInSubtree(tree);\n\n return { nextSteps };\n}\n"],"names":["readFileSync","join","getProjects","joinPathFragments","TS_AGENT_GENERATOR_INFO","addDestructuredImport","applyGritQL","captureGritQLVariable","matchGritQL","formatFilesInSubtree","EXPRESS_IMPORT_OLD","EXPRESS_IMPORT_NEW","RANDOM_UUID_IMPORT","SESSION_ID_HEADER_LINE","MIDDLEWARE_LEADING_COMMENT","AGUI_MIDDLEWARE_DEFINITION_OLD","A2A_MIDDLEWARE_USE_OLD","A2A_MIDDLEWARE_USE_NEW","RUNWITHSESSIONID_IMPORT_CAPTURE","REMOVE_RUNWITHSESSIONID_IMPORT_PATTERN","SESSION_ID_MIDDLEWARE_TEMPLATE","dirname","sessionIdMiddlewareContent","agentConnectionImport","replace","findAgentComponents","components","filter","component","generator","id","protocol","migrateIndex","tree","indexPath","nextSteps","exists","contents","read","includes","middlewarePattern","diverged","push","ready","mod","dir","split","slice","middlewarePath","write","migration","project","values","metadata","path","root"],"mappings":"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"}
|
|
@@ -693,18 +693,12 @@ Refer to tools as your 'spellbook'.
|
|
|
693
693
|
`;
|
|
694
694
|
|
|
695
695
|
exports[`py#agent generator > should match snapshot for generated files > agent-main.py 1`] = `
|
|
696
|
-
"import
|
|
697
|
-
|
|
698
|
-
import uvicorn
|
|
696
|
+
"import uvicorn
|
|
699
697
|
from bedrock_agentcore.runtime.models import PingStatus
|
|
700
|
-
from fastapi import Request
|
|
701
|
-
from proj_agent_connection import session_id_context
|
|
702
698
|
from pydantic import BaseModel, Field
|
|
703
|
-
from starlette.middleware.base import BaseHTTPMiddleware
|
|
704
699
|
|
|
705
700
|
from .init import JsonStreamingResponse, app
|
|
706
|
-
|
|
707
|
-
SESSION_ID_HEADER = "x-amzn-bedrock-agentcore-runtime-session-id"
|
|
701
|
+
from .middleware.session_id_middleware import SessionIdMiddleware
|
|
708
702
|
|
|
709
703
|
|
|
710
704
|
class InvokeInput(BaseModel):
|
|
@@ -745,16 +739,7 @@ async def invoke(input: InvokeInput) -> JsonStreamingResponse:
|
|
|
745
739
|
return JsonStreamingResponse(handle_invoke(input))
|
|
746
740
|
|
|
747
741
|
|
|
748
|
-
|
|
749
|
-
"""Bind the inbound session (or a fresh UUID) to async context."""
|
|
750
|
-
|
|
751
|
-
async def dispatch(self, request: Request, call_next):
|
|
752
|
-
session_id = request.headers.get(SESSION_ID_HEADER) or str(uuid.uuid4())
|
|
753
|
-
with session_id_context(session_id):
|
|
754
|
-
return await call_next(request)
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
app.add_middleware(_SessionIdMiddleware)
|
|
742
|
+
app.add_middleware(SessionIdMiddleware)
|
|
758
743
|
|
|
759
744
|
|
|
760
745
|
@app.get("/ping")
|
|
@@ -618,14 +618,12 @@ from fastapi import FastAPI, Request
|
|
|
618
618
|
from fastapi.middleware.cors import CORSMiddleware
|
|
619
619
|
from fastapi.responses import JSONResponse, StreamingResponse
|
|
620
620
|
from proj_agent_connection import get_current_session_id, session_id_context
|
|
621
|
-
from starlette.middleware.base import BaseHTTPMiddleware
|
|
622
621
|
|
|
623
622
|
from .agent import get_agent
|
|
623
|
+
from .middleware.session_id_middleware import SESSION_ID_HEADER, SessionIdMiddleware
|
|
624
624
|
|
|
625
625
|
logging.basicConfig(level=logging.INFO)
|
|
626
626
|
|
|
627
|
-
SESSION_ID_HEADER = "x-amzn-bedrock-agentcore-runtime-session-id"
|
|
628
|
-
|
|
629
627
|
|
|
630
628
|
@asynccontextmanager
|
|
631
629
|
async def lifespan(app: FastAPI):
|
|
@@ -638,15 +636,6 @@ async def lifespan(app: FastAPI):
|
|
|
638
636
|
yield
|
|
639
637
|
|
|
640
638
|
|
|
641
|
-
class _SessionIdMiddleware(BaseHTTPMiddleware):
|
|
642
|
-
"""Bind the session ID for this request so downstream MCP / A2A clients forward it on outbound calls."""
|
|
643
|
-
|
|
644
|
-
async def dispatch(self, request: Request, call_next):
|
|
645
|
-
session_id = request.headers.get(SESSION_ID_HEADER) or str(uuid.uuid4())
|
|
646
|
-
with session_id_context(session_id):
|
|
647
|
-
return await call_next(request)
|
|
648
|
-
|
|
649
|
-
|
|
650
639
|
app = FastAPI(title="SnapshotAgent", lifespan=lifespan)
|
|
651
640
|
# Allow browser-based AG-UI clients (e.g. a connected React website) to call
|
|
652
641
|
# this agent cross-origin, including the CORS preflight.
|
|
@@ -657,7 +646,7 @@ app.add_middleware(
|
|
|
657
646
|
allow_methods=["*"],
|
|
658
647
|
allow_headers=["*"],
|
|
659
648
|
)
|
|
660
|
-
app.add_middleware(
|
|
649
|
+
app.add_middleware(SessionIdMiddleware)
|
|
661
650
|
|
|
662
651
|
|
|
663
652
|
@app.post("/invocations")
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import logging
|
|
2
2
|
import os
|
|
3
|
-
import uuid
|
|
4
3
|
from contextlib import asynccontextmanager
|
|
5
4
|
|
|
6
5
|
from a2a.server.agent_execution import AgentExecutor, RequestContext
|
|
@@ -10,18 +9,17 @@ from a2a.server.request_handlers import DefaultRequestHandler
|
|
|
10
9
|
from a2a.server.tasks import InMemoryTaskStore, TaskUpdater
|
|
11
10
|
from a2a.types import AgentCapabilities, AgentCard, Part, TextPart
|
|
12
11
|
from a2a.utils import new_task
|
|
13
|
-
from fastapi import FastAPI
|
|
14
|
-
from starlette.middleware.base import BaseHTTPMiddleware
|
|
12
|
+
from fastapi import FastAPI
|
|
15
13
|
|
|
16
|
-
from <%- agentConnectionModuleName %> import get_current_session_id
|
|
14
|
+
from <%- agentConnectionModuleName %> import get_current_session_id
|
|
17
15
|
|
|
18
16
|
from .agent import get_agent
|
|
17
|
+
from .middleware.session_id_middleware import SessionIdMiddleware
|
|
19
18
|
|
|
20
19
|
logging.basicConfig(level=logging.INFO)
|
|
21
20
|
|
|
22
21
|
PORT = int(os.environ.get("PORT", "9000"))
|
|
23
22
|
RUNTIME_URL = os.environ.get("AGENTCORE_RUNTIME_URL", f"http://localhost:{PORT}/")
|
|
24
|
-
SESSION_ID_HEADER = "x-amzn-bedrock-agentcore-runtime-session-id"
|
|
25
23
|
DEFAULT_MODES = ["text/plain"]
|
|
26
24
|
|
|
27
25
|
|
|
@@ -73,17 +71,8 @@ _agent_card = AgentCard(
|
|
|
73
71
|
a2a_app = A2AStarletteApplication(agent_card=_agent_card, http_handler=_handler).build()
|
|
74
72
|
|
|
75
73
|
|
|
76
|
-
class _SessionIdMiddleware(BaseHTTPMiddleware):
|
|
77
|
-
"""Bind the inbound session (or a fresh UUID) to async context."""
|
|
78
|
-
|
|
79
|
-
async def dispatch(self, request: Request, call_next):
|
|
80
|
-
session_id = request.headers.get(SESSION_ID_HEADER) or str(uuid.uuid4())
|
|
81
|
-
with session_id_context(session_id):
|
|
82
|
-
return await call_next(request)
|
|
83
|
-
|
|
84
|
-
|
|
85
74
|
app = FastAPI(title="<%= agentNameClassName %>", lifespan=lifespan)
|
|
86
|
-
app.add_middleware(
|
|
75
|
+
app.add_middleware(SessionIdMiddleware)
|
|
87
76
|
|
|
88
77
|
|
|
89
78
|
@app.get("/ping")
|
|
@@ -10,14 +10,12 @@ from fastapi import FastAPI, Request
|
|
|
10
10
|
from fastapi.middleware.cors import CORSMiddleware
|
|
11
11
|
from fastapi.responses import JSONResponse, StreamingResponse
|
|
12
12
|
from <%- agentConnectionModuleName %> import get_current_session_id, session_id_context
|
|
13
|
-
from starlette.middleware.base import BaseHTTPMiddleware
|
|
14
13
|
|
|
15
14
|
from .agent import get_agent
|
|
15
|
+
from .middleware.session_id_middleware import SESSION_ID_HEADER, SessionIdMiddleware
|
|
16
16
|
|
|
17
17
|
logging.basicConfig(level=logging.INFO)
|
|
18
18
|
|
|
19
|
-
SESSION_ID_HEADER = "x-amzn-bedrock-agentcore-runtime-session-id"
|
|
20
|
-
|
|
21
19
|
|
|
22
20
|
@asynccontextmanager
|
|
23
21
|
async def lifespan(app: FastAPI):
|
|
@@ -30,15 +28,6 @@ async def lifespan(app: FastAPI):
|
|
|
30
28
|
yield
|
|
31
29
|
|
|
32
30
|
|
|
33
|
-
class _SessionIdMiddleware(BaseHTTPMiddleware):
|
|
34
|
-
"""Bind the session ID for this request so downstream MCP / A2A clients forward it on outbound calls."""
|
|
35
|
-
|
|
36
|
-
async def dispatch(self, request: Request, call_next):
|
|
37
|
-
session_id = request.headers.get(SESSION_ID_HEADER) or str(uuid.uuid4())
|
|
38
|
-
with session_id_context(session_id):
|
|
39
|
-
return await call_next(request)
|
|
40
|
-
|
|
41
|
-
|
|
42
31
|
app = FastAPI(title="<%= agentNameClassName %>", lifespan=lifespan)
|
|
43
32
|
# Allow browser-based AG-UI clients (e.g. a connected React website) to call
|
|
44
33
|
# this agent cross-origin, including the CORS preflight.
|
|
@@ -49,7 +38,7 @@ app.add_middleware(
|
|
|
49
38
|
allow_methods=["*"],
|
|
50
39
|
allow_headers=["*"],
|
|
51
40
|
)
|
|
52
|
-
app.add_middleware(
|
|
41
|
+
app.add_middleware(SessionIdMiddleware)
|
|
53
42
|
|
|
54
43
|
|
|
55
44
|
@app.post("/invocations")
|
|
File without changes
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import uuid
|
|
2
|
+
|
|
3
|
+
from fastapi import Request
|
|
4
|
+
from starlette.middleware.base import BaseHTTPMiddleware
|
|
5
|
+
|
|
6
|
+
from <%- agentConnectionModuleName %> import session_id_context
|
|
7
|
+
|
|
8
|
+
SESSION_ID_HEADER = "x-amzn-bedrock-agentcore-runtime-session-id"
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class SessionIdMiddleware(BaseHTTPMiddleware):
|
|
12
|
+
"""Bind the session ID for this request so downstream MCP / A2A clients forward it on outbound calls."""
|
|
13
|
+
|
|
14
|
+
async def dispatch(self, request: Request, call_next):
|
|
15
|
+
session_id = request.headers.get(SESSION_ID_HEADER) or str(uuid.uuid4())
|
|
16
|
+
with session_id_context(session_id):
|
|
17
|
+
return await call_next(request)
|
|
@@ -2,14 +2,11 @@ import uuid
|
|
|
2
2
|
|
|
3
3
|
import uvicorn
|
|
4
4
|
from bedrock_agentcore.runtime.models import PingStatus
|
|
5
|
-
from fastapi import Request
|
|
6
5
|
from pydantic import BaseModel, Field
|
|
7
|
-
from starlette.middleware.base import BaseHTTPMiddleware
|
|
8
6
|
from <%- agentConnectionModuleName %> import get_current_session_id, session_id_context
|
|
9
7
|
|
|
10
8
|
from .init import JsonStreamingResponse, app
|
|
11
|
-
|
|
12
|
-
SESSION_ID_HEADER = "x-amzn-bedrock-agentcore-runtime-session-id"
|
|
9
|
+
from .middleware.session_id_middleware import SessionIdMiddleware
|
|
13
10
|
|
|
14
11
|
|
|
15
12
|
class InvokeInput(BaseModel):
|
|
@@ -59,16 +56,7 @@ async def invoke(input: InvokeInput) -> JsonStreamingResponse:
|
|
|
59
56
|
return JsonStreamingResponse(handle_invoke(input, session_id))
|
|
60
57
|
|
|
61
58
|
|
|
62
|
-
|
|
63
|
-
"""Bind the inbound session (or a fresh UUID) to async context."""
|
|
64
|
-
|
|
65
|
-
async def dispatch(self, request: Request, call_next):
|
|
66
|
-
session_id = request.headers.get(SESSION_ID_HEADER) or str(uuid.uuid4())
|
|
67
|
-
with session_id_context(session_id):
|
|
68
|
-
return await call_next(request)
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
app.add_middleware(_SessionIdMiddleware)
|
|
59
|
+
app.add_middleware(SessionIdMiddleware)
|
|
72
60
|
|
|
73
61
|
|
|
74
62
|
@app.get("/ping")
|
|
@@ -1,23 +1,21 @@
|
|
|
1
1
|
import logging
|
|
2
2
|
import os
|
|
3
|
-
import uuid
|
|
4
3
|
from contextlib import asynccontextmanager
|
|
5
4
|
from types import SimpleNamespace
|
|
6
5
|
from typing import cast
|
|
7
6
|
|
|
8
|
-
from fastapi import FastAPI
|
|
9
|
-
from starlette.middleware.base import BaseHTTPMiddleware
|
|
7
|
+
from fastapi import FastAPI
|
|
10
8
|
from strands import Agent
|
|
11
9
|
from strands.multiagent.a2a import A2AServer
|
|
12
|
-
from <%- agentConnectionModuleName %> import
|
|
10
|
+
from <%- agentConnectionModuleName %> import with_session_id
|
|
13
11
|
|
|
14
12
|
from .agent import get_agent
|
|
13
|
+
from .middleware.session_id_middleware import SessionIdMiddleware
|
|
15
14
|
|
|
16
15
|
logging.basicConfig(level=logging.INFO)
|
|
17
16
|
|
|
18
17
|
PORT = int(os.environ.get("PORT", "9000"))
|
|
19
18
|
RUNTIME_URL = os.environ.get("AGENTCORE_RUNTIME_URL", f"http://localhost:{PORT}/")
|
|
20
|
-
SESSION_ID_HEADER = "x-amzn-bedrock-agentcore-runtime-session-id"
|
|
21
19
|
AGENT_NAME = "<%= agentNameClassName %>"
|
|
22
20
|
AGENT_DESCRIPTION = "A Strands Agent exposed via the Agent-to-Agent (A2A) protocol."
|
|
23
21
|
|
|
@@ -41,17 +39,8 @@ async def lifespan(app: FastAPI):
|
|
|
41
39
|
yield
|
|
42
40
|
|
|
43
41
|
|
|
44
|
-
class _SessionIdMiddleware(BaseHTTPMiddleware):
|
|
45
|
-
"""Bind the inbound session (or a fresh UUID) to async context."""
|
|
46
|
-
|
|
47
|
-
async def dispatch(self, request: Request, call_next):
|
|
48
|
-
session_id = request.headers.get(SESSION_ID_HEADER) or str(uuid.uuid4())
|
|
49
|
-
with session_id_context(session_id):
|
|
50
|
-
return await call_next(request)
|
|
51
|
-
|
|
52
|
-
|
|
53
42
|
app = FastAPI(lifespan=lifespan)
|
|
54
|
-
app.add_middleware(
|
|
43
|
+
app.add_middleware(SessionIdMiddleware)
|
|
55
44
|
|
|
56
45
|
|
|
57
46
|
@app.get("/ping")
|
|
@@ -9,15 +9,13 @@ from fastapi import FastAPI, Request
|
|
|
9
9
|
from fastapi.middleware.cors import CORSMiddleware
|
|
10
10
|
from fastapi.responses import StreamingResponse
|
|
11
11
|
from <%- agentConnectionModuleName %> import get_current_session_id, session_id_context
|
|
12
|
-
from starlette.middleware.base import BaseHTTPMiddleware
|
|
13
12
|
|
|
14
13
|
from .agent import get_agent
|
|
14
|
+
from .middleware.session_id_middleware import SESSION_ID_HEADER, SessionIdMiddleware
|
|
15
15
|
from .session import get_session_manager
|
|
16
16
|
|
|
17
17
|
logging.basicConfig(level=logging.INFO)
|
|
18
18
|
|
|
19
|
-
SESSION_ID_HEADER = "x-amzn-bedrock-agentcore-runtime-session-id"
|
|
20
|
-
|
|
21
19
|
|
|
22
20
|
@asynccontextmanager
|
|
23
21
|
async def lifespan(app: FastAPI):
|
|
@@ -33,15 +31,6 @@ async def lifespan(app: FastAPI):
|
|
|
33
31
|
yield
|
|
34
32
|
|
|
35
33
|
|
|
36
|
-
class _SessionIdMiddleware(BaseHTTPMiddleware):
|
|
37
|
-
"""Bind the session ID for this request so downstream MCP / A2A clients forward it on outbound calls."""
|
|
38
|
-
|
|
39
|
-
async def dispatch(self, request: Request, call_next):
|
|
40
|
-
session_id = request.headers.get(SESSION_ID_HEADER) or str(uuid.uuid4())
|
|
41
|
-
with session_id_context(session_id):
|
|
42
|
-
return await call_next(request)
|
|
43
|
-
|
|
44
|
-
|
|
45
34
|
app = FastAPI(title="AWS Strands - <%= agentNameClassName %>", lifespan=lifespan)
|
|
46
35
|
app.add_middleware(
|
|
47
36
|
CORSMiddleware,
|
|
@@ -50,7 +39,7 @@ app.add_middleware(
|
|
|
50
39
|
allow_methods=["*"],
|
|
51
40
|
allow_headers=["*"],
|
|
52
41
|
)
|
|
53
|
-
app.add_middleware(
|
|
42
|
+
app.add_middleware(SessionIdMiddleware)
|
|
54
43
|
|
|
55
44
|
|
|
56
45
|
@app.post("/invocations")
|
|
File without changes
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import uuid
|
|
2
|
+
|
|
3
|
+
from fastapi import Request
|
|
4
|
+
from starlette.middleware.base import BaseHTTPMiddleware
|
|
5
|
+
|
|
6
|
+
from <%- agentConnectionModuleName %> import session_id_context
|
|
7
|
+
|
|
8
|
+
SESSION_ID_HEADER = "x-amzn-bedrock-agentcore-runtime-session-id"
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class SessionIdMiddleware(BaseHTTPMiddleware):
|
|
12
|
+
"""Bind the session ID for this request so downstream MCP / A2A clients forward it on outbound calls."""
|
|
13
|
+
|
|
14
|
+
async def dispatch(self, request: Request, call_next):
|
|
15
|
+
session_id = request.headers.get(SESSION_ID_HEADER) or str(uuid.uuid4())
|
|
16
|
+
with session_id_context(session_id):
|
|
17
|
+
return await call_next(request)
|
|
@@ -1,15 +1,9 @@
|
|
|
1
|
-
import uuid
|
|
2
|
-
|
|
3
1
|
import uvicorn
|
|
4
2
|
from bedrock_agentcore.runtime.models import PingStatus
|
|
5
|
-
from fastapi import Request
|
|
6
3
|
from pydantic import BaseModel, Field
|
|
7
|
-
from starlette.middleware.base import BaseHTTPMiddleware
|
|
8
|
-
from <%- agentConnectionModuleName %> import session_id_context
|
|
9
4
|
|
|
10
5
|
from .init import JsonStreamingResponse, app
|
|
11
|
-
|
|
12
|
-
SESSION_ID_HEADER = "x-amzn-bedrock-agentcore-runtime-session-id"
|
|
6
|
+
from .middleware.session_id_middleware import SessionIdMiddleware
|
|
13
7
|
|
|
14
8
|
|
|
15
9
|
class InvokeInput(BaseModel):
|
|
@@ -41,16 +35,7 @@ async def invoke(input: InvokeInput) -> JsonStreamingResponse:
|
|
|
41
35
|
return JsonStreamingResponse(handle_invoke(input))
|
|
42
36
|
|
|
43
37
|
|
|
44
|
-
|
|
45
|
-
"""Bind the inbound session (or a fresh UUID) to async context."""
|
|
46
|
-
|
|
47
|
-
async def dispatch(self, request: Request, call_next):
|
|
48
|
-
session_id = request.headers.get(SESSION_ID_HEADER) or str(uuid.uuid4())
|
|
49
|
-
with session_id_context(session_id):
|
|
50
|
-
return await call_next(request)
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
app.add_middleware(_SessionIdMiddleware)
|
|
38
|
+
app.add_middleware(SessionIdMiddleware)
|
|
54
39
|
|
|
55
40
|
|
|
56
41
|
@app.get("/ping")
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
|
3
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
4
|
+
*/
|
|
5
|
+
export { agentcoreHarnessGenerator } from '../agentcore-harness/generator';
|
|
6
|
+
export type { AgentcoreHarnessGeneratorSchema } from '../agentcore-harness/schema';
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
|
3
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
4
|
+
*/ // AgentCore Harness Generator
|
|
5
|
+
export { agentcoreHarnessGenerator } from "../agentcore-harness/generator.js";
|
|
6
|
+
|
|
7
|
+
//# sourceMappingURL=agentcore-harness.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../../../packages/nx-plugin/src/sdk/agentcore-harness.ts"],"sourcesContent":["/**\n * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.\n * SPDX-License-Identifier: Apache-2.0\n */\n\n// AgentCore Harness Generator\nexport { agentcoreHarnessGenerator } from '../agentcore-harness/generator';\nexport type { AgentcoreHarnessGeneratorSchema } from '../agentcore-harness/schema';\n"],"names":["agentcoreHarnessGenerator"],"mappings":"AAAA;;;CAGC,GAED,8BAA8B;AAC9B,SAASA,yBAAyB,QAAQ,oCAAiC"}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
|
3
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
4
|
+
*/
|
|
5
|
+
export { smithyProjectGenerator } from '../smithy/project/generator';
|
|
6
|
+
export type { SmithyProjectGeneratorSchema } from '../smithy/project/schema';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../../../packages/nx-plugin/src/sdk/smithy.ts"],"sourcesContent":["/**\n * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.\n * SPDX-License-Identifier: Apache-2.0\n */\n\n// Smithy Project Generator\nexport { smithyProjectGenerator } from '../smithy/project/generator';\nexport type { SmithyProjectGeneratorSchema } from '../smithy/project/schema';\n"],"names":["smithyProjectGenerator"],"mappings":"AAAA;;;CAGC,GAED,2BAA2B;AAC3B,SAASA,sBAAsB,QAAQ,iCAA8B"}
|
|
@@ -1,14 +1,12 @@
|
|
|
1
1
|
import { A2AExpressServer } from '@strands-agents/sdk/a2a/express';
|
|
2
|
-
import express
|
|
3
|
-
import {
|
|
4
|
-
import { runWithSessionId, withSessionId } from '<%- agentConnectionImport %>';
|
|
2
|
+
import express from 'express';
|
|
3
|
+
import { withSessionId } from '<%- agentConnectionImport %>';
|
|
5
4
|
import { getAgent } from './agent<% if (esm) { %>.js<% } %>';
|
|
5
|
+
import { sessionIdMiddleware } from './middleware/session-id-middleware<% if (esm) { %>.js<% } %>';
|
|
6
6
|
|
|
7
7
|
const PORT = parseInt(process.env.PORT || '9000');
|
|
8
8
|
const HOST = '0.0.0.0';
|
|
9
9
|
|
|
10
|
-
const SESSION_ID_HEADER = 'x-amzn-bedrock-agentcore-runtime-session-id';
|
|
11
|
-
|
|
12
10
|
void (async () => {
|
|
13
11
|
const httpUrl =
|
|
14
12
|
process.env.AGENTCORE_RUNTIME_URL ?? `http://localhost:${PORT}/`;
|
|
@@ -25,12 +23,7 @@ void (async () => {
|
|
|
25
23
|
|
|
26
24
|
const app = express();
|
|
27
25
|
app.get('/ping', (_req, res) => res.status(200).json({ status: 'Healthy' }));
|
|
28
|
-
|
|
29
|
-
app.use((req: Request, _res: Response, next: NextFunction) => {
|
|
30
|
-
const header = req.headers[SESSION_ID_HEADER];
|
|
31
|
-
const sessionId = (Array.isArray(header) ? header[0] : header) ?? randomUUID();
|
|
32
|
-
runWithSessionId(sessionId, () => next());
|
|
33
|
-
});
|
|
26
|
+
app.use(sessionIdMiddleware);
|
|
34
27
|
app.use(server.createMiddleware());
|
|
35
28
|
app.listen(PORT, HOST, () => {
|
|
36
29
|
console.log(`A2A server listening on ${HOST}:${PORT}`);
|
|
@@ -4,29 +4,19 @@ import {
|
|
|
4
4
|
addPing,
|
|
5
5
|
addCapabilities,
|
|
6
6
|
} from '@ag-ui/aws-strands/server';
|
|
7
|
-
import express
|
|
7
|
+
import express from 'express';
|
|
8
8
|
import cors from 'cors';
|
|
9
|
-
import { randomUUID } from 'node:crypto';
|
|
10
9
|
import {
|
|
11
10
|
ModelErrorLoggingPlugin,
|
|
12
11
|
ToolErrorLoggingPlugin,
|
|
13
|
-
runWithSessionId,
|
|
14
12
|
} from '<%- agentConnectionImport %>';
|
|
15
13
|
import { getAgent } from './agent<% if (esm) { %>.js<% } %>';
|
|
16
14
|
import { getSessionManager } from './session<% if (esm) { %>.js<% } %>';
|
|
15
|
+
import { sessionIdMiddleware } from './middleware/session-id-middleware<% if (esm) { %>.js<% } %>';
|
|
17
16
|
|
|
18
17
|
const PORT = parseInt(process.env.PORT || '8080');
|
|
19
18
|
const HOST = '0.0.0.0';
|
|
20
19
|
|
|
21
|
-
const SESSION_ID_HEADER = 'x-amzn-bedrock-agentcore-runtime-session-id';
|
|
22
|
-
|
|
23
|
-
// Bind the inbound session (or a fresh UUID) for downstream MCP / A2A calls.
|
|
24
|
-
const sessionIdMiddleware = (req: Request, _res: Response, next: NextFunction) => {
|
|
25
|
-
const header = req.headers[SESSION_ID_HEADER];
|
|
26
|
-
const sessionId = (Array.isArray(header) ? header[0] : header) ?? randomUUID();
|
|
27
|
-
runWithSessionId(sessionId, () => next());
|
|
28
|
-
};
|
|
29
|
-
|
|
30
20
|
void (async () => {
|
|
31
21
|
const agent = await getAgent();
|
|
32
22
|
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { randomUUID } from 'node:crypto';
|
|
2
|
+
import type { NextFunction, Request, Response } from 'express';
|
|
3
|
+
import { runWithSessionId } from '<%- agentConnectionImport %>';
|
|
4
|
+
|
|
5
|
+
export const SESSION_ID_HEADER = 'x-amzn-bedrock-agentcore-runtime-session-id';
|
|
6
|
+
|
|
7
|
+
// Bind the inbound session (or a fresh UUID) for downstream MCP / A2A calls.
|
|
8
|
+
export const sessionIdMiddleware = (
|
|
9
|
+
req: Request,
|
|
10
|
+
_res: Response,
|
|
11
|
+
next: NextFunction,
|
|
12
|
+
) => {
|
|
13
|
+
const header = req.headers[SESSION_ID_HEADER];
|
|
14
|
+
const sessionId = (Array.isArray(header) ? header[0] : header) ?? randomUUID();
|
|
15
|
+
runWithSessionId(sessionId, () => next());
|
|
16
|
+
};
|