@aws-blocks/bb-agent 0.3.0 → 0.3.1

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.
@@ -1,6 +1,16 @@
1
1
  import type { ScopeParent } from '@aws-blocks/core';
2
+ import type { FileBucket } from '@aws-blocks/bb-file-bucket';
3
+ import type { SnapshotStorage } from '@strands-agents/sdk';
4
+ import type { S3StorageConfig } from '@strands-agents/sdk/session/s3-storage';
2
5
  import { AgentBase } from './agent.js';
3
6
  import type { AgentConfig, DefaultToolContext } from './types.js';
7
+ /**
8
+ * Builds the deployed Agent's snapshot storage, pinning S3Storage to the Lambda
9
+ * execution region (`AWS_REGION`) so non-us-east-1 deploys use the correct regional
10
+ * endpoint (#120). `S3StorageImpl` is injectable so tests can assert the resulting
11
+ * config without depending on S3Storage/AWS SDK internals; production uses the real one.
12
+ */
13
+ export declare function createDeployedSnapshotStorage(bucket: FileBucket, S3StorageImpl?: new (config: S3StorageConfig) => SnapshotStorage): SnapshotStorage;
4
14
  export declare class Agent<TContext = DefaultToolContext> extends AgentBase<TContext> {
5
15
  constructor(scope: ScopeParent, id: string, config: AgentConfig<TContext>);
6
16
  }
@@ -1 +1 @@
1
- {"version":3,"file":"agent.aws.d.ts","sourceRoot":"","sources":["../src/agent.aws.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AACpD,OAAO,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAEvC,OAAO,KAAK,EAAE,WAAW,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAC;AAGlE,qBAAa,KAAK,CAAC,QAAQ,GAAG,kBAAkB,CAAE,SAAQ,SAAS,CAAC,QAAQ,CAAC;gBAChE,KAAK,EAAE,WAAW,EAAE,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE,WAAW,CAAC,QAAQ,CAAC;CAGzE"}
1
+ {"version":3,"file":"agent.aws.d.ts","sourceRoot":"","sources":["../src/agent.aws.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AACpD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,4BAA4B,CAAC;AAC7D,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAC;AAE3D,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,wCAAwC,CAAC;AAC9E,OAAO,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AACvC,OAAO,KAAK,EAAE,WAAW,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAC;AAGlE;;;;;GAKG;AACH,wBAAgB,6BAA6B,CAC5C,MAAM,EAAE,UAAU,EAClB,aAAa,GAAE,KAAK,MAAM,EAAE,eAAe,KAAK,eAA2B,GACzE,eAAe,CAEjB;AAED,qBAAa,KAAK,CAAC,QAAQ,GAAG,kBAAkB,CAAE,SAAQ,SAAS,CAAC,QAAQ,CAAC;gBAChE,KAAK,EAAE,WAAW,EAAE,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE,WAAW,CAAC,QAAQ,CAAC;CAGzE"}
package/dist/agent.aws.js CHANGED
@@ -1,10 +1,19 @@
1
1
  // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2
2
  // SPDX-License-Identifier: Apache-2.0
3
- import { AgentBase } from './agent.js';
4
3
  import { S3Storage } from '@strands-agents/sdk/session/s3-storage';
4
+ import { AgentBase } from './agent.js';
5
5
  import { BedrockModels } from './models.js';
6
+ /**
7
+ * Builds the deployed Agent's snapshot storage, pinning S3Storage to the Lambda
8
+ * execution region (`AWS_REGION`) so non-us-east-1 deploys use the correct regional
9
+ * endpoint (#120). `S3StorageImpl` is injectable so tests can assert the resulting
10
+ * config without depending on S3Storage/AWS SDK internals; production uses the real one.
11
+ */
12
+ export function createDeployedSnapshotStorage(bucket, S3StorageImpl = S3Storage) {
13
+ return new S3StorageImpl({ bucket: bucket.fullId, region: process.env.AWS_REGION });
14
+ }
6
15
  export class Agent extends AgentBase {
7
16
  constructor(scope, id, config) {
8
- super(scope, id, config, config.model?.deployed ?? BedrockModels.BALANCED, (bucket) => new S3Storage({ bucket: bucket.fullId }));
17
+ super(scope, id, config, config.model?.deployed ?? BedrockModels.BALANCED, createDeployedSnapshotStorage);
9
18
  }
10
19
  }
@@ -899,3 +899,69 @@ describe('OllamaModels presets', () => {
899
899
  }
900
900
  });
901
901
  });
902
+ // ── deployed Agent S3Storage region (multi-region) ──────────────────────────
903
+ // Regression for #120: the deployed (aws-runtime) Agent must build Strands' S3Storage
904
+ // with the Lambda execution region (AWS_REGION). Omitting `region` defaults S3Storage to
905
+ // us-east-1 and breaks deploys elsewhere — the session bucket lives in the deploy region,
906
+ // so snapshots hit a cross-region 301 PermanentRedirect. index.test.ts otherwise only
907
+ // drives the mock/local path, so this covers agent.aws.ts by spying the S3Storage ctor.
908
+ import { Agent as DeployedAgent } from './index.aws.js';
909
+ import { createDeployedSnapshotStorage } from './agent.aws.js';
910
+ describe('deployed Agent S3Storage region (multi-region)', () => {
911
+ // Type-safe stand-in for S3Storage that records every config it's constructed with,
912
+ // so we assert exactly what agent.aws.ts passes in — no S3Storage/AWS SDK internals.
913
+ function s3StorageSpy() {
914
+ const configs = [];
915
+ class Spy {
916
+ constructor(config) {
917
+ configs.push(config);
918
+ }
919
+ async saveSnapshot() { }
920
+ async loadSnapshot() {
921
+ return null;
922
+ }
923
+ async listSnapshotIds() {
924
+ return [];
925
+ }
926
+ async deleteSession() { }
927
+ async loadManifest() {
928
+ return { schemaVersion: '1.0', updatedAt: new Date().toISOString() };
929
+ }
930
+ async saveManifest() { }
931
+ }
932
+ return { configs, Spy };
933
+ }
934
+ // Capture the S3StorageConfig the deployed factory builds for a given AWS_REGION.
935
+ function configForRegion(region, scopeId) {
936
+ const prev = process.env.AWS_REGION;
937
+ process.env.AWS_REGION = region;
938
+ try {
939
+ const { configs, Spy } = s3StorageSpy();
940
+ const bucket = new FileBucket(new Scope(scopeId), 'sn');
941
+ createDeployedSnapshotStorage(bucket, Spy);
942
+ assert.strictEqual(configs.length, 1, 'S3Storage should be constructed exactly once');
943
+ assert.strictEqual(configs[0].bucket, bucket.fullId, 'session bucket id should be passed through');
944
+ return configs[0];
945
+ }
946
+ finally {
947
+ if (prev === undefined)
948
+ delete process.env.AWS_REGION;
949
+ else
950
+ process.env.AWS_REGION = prev;
951
+ }
952
+ }
953
+ test('constructs S3Storage with the Lambda execution region (eu-west-1)', () => {
954
+ const config = configForRegion('eu-west-1', 'test-s3-region-euw1');
955
+ assert.strictEqual(config.region, 'eu-west-1');
956
+ assert.notStrictEqual(config.region, 'us-east-1');
957
+ });
958
+ test('does not hard-pin us-east-1 when deployed to another region (ap-southeast-2)', () => {
959
+ const config = configForRegion('ap-southeast-2', 'test-s3-region-apse2');
960
+ assert.strictEqual(config.region, 'ap-southeast-2');
961
+ assert.notStrictEqual(config.region, 'us-east-1');
962
+ });
963
+ test('deployed Agent constructs on the aws-runtime path', () => {
964
+ const agent = new DeployedAgent(new Scope('test-s3-agent'), 'r', { systemPrompt: 'test', model: { deployed: { provider: 'canned' } } });
965
+ assert.ok(agent);
966
+ });
967
+ });
package/dist/version.d.ts CHANGED
@@ -1,3 +1,3 @@
1
1
  export declare const BB_NAME = "Agent";
2
- export declare const BB_VERSION = "0.3.0";
2
+ export declare const BB_VERSION = "0.3.1";
3
3
  //# sourceMappingURL=version.d.ts.map
package/dist/version.js CHANGED
@@ -1,3 +1,3 @@
1
1
  // Auto-generated by scripts/generate-version.mjs — do not edit manually
2
2
  export const BB_NAME = 'Agent';
3
- export const BB_VERSION = '0.3.0';
3
+ export const BB_VERSION = '0.3.1';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aws-blocks/bb-agent",
3
- "version": "0.3.0",
3
+ "version": "0.3.1",
4
4
  "author": "Amazon Web Services",
5
5
  "license": "Apache-2.0",
6
6
  "type": "module",
package/src/agent.aws.ts CHANGED
@@ -2,13 +2,29 @@
2
2
  // SPDX-License-Identifier: Apache-2.0
3
3
 
4
4
  import type { ScopeParent } from '@aws-blocks/core';
5
- import { AgentBase } from './agent.js';
5
+ import type { FileBucket } from '@aws-blocks/bb-file-bucket';
6
+ import type { SnapshotStorage } from '@strands-agents/sdk';
6
7
  import { S3Storage } from '@strands-agents/sdk/session/s3-storage';
8
+ import type { S3StorageConfig } from '@strands-agents/sdk/session/s3-storage';
9
+ import { AgentBase } from './agent.js';
7
10
  import type { AgentConfig, DefaultToolContext } from './types.js';
8
11
  import { BedrockModels } from './models.js';
9
12
 
13
+ /**
14
+ * Builds the deployed Agent's snapshot storage, pinning S3Storage to the Lambda
15
+ * execution region (`AWS_REGION`) so non-us-east-1 deploys use the correct regional
16
+ * endpoint (#120). `S3StorageImpl` is injectable so tests can assert the resulting
17
+ * config without depending on S3Storage/AWS SDK internals; production uses the real one.
18
+ */
19
+ export function createDeployedSnapshotStorage(
20
+ bucket: FileBucket,
21
+ S3StorageImpl: new (config: S3StorageConfig) => SnapshotStorage = S3Storage,
22
+ ): SnapshotStorage {
23
+ return new S3StorageImpl({ bucket: bucket.fullId, region: process.env.AWS_REGION });
24
+ }
25
+
10
26
  export class Agent<TContext = DefaultToolContext> extends AgentBase<TContext> {
11
27
  constructor(scope: ScopeParent, id: string, config: AgentConfig<TContext>) {
12
- super(scope, id, config, config.model?.deployed ?? BedrockModels.BALANCED, (bucket) => new S3Storage({ bucket: bucket.fullId }));
28
+ super(scope, id, config, config.model?.deployed ?? BedrockModels.BALANCED, createDeployedSnapshotStorage);
13
29
  }
14
30
  }
package/src/index.test.ts CHANGED
@@ -1052,3 +1052,75 @@ describe('OllamaModels presets', () => {
1052
1052
  }
1053
1053
  });
1054
1054
  });
1055
+
1056
+ // ── deployed Agent S3Storage region (multi-region) ──────────────────────────
1057
+ // Regression for #120: the deployed (aws-runtime) Agent must build Strands' S3Storage
1058
+ // with the Lambda execution region (AWS_REGION). Omitting `region` defaults S3Storage to
1059
+ // us-east-1 and breaks deploys elsewhere — the session bucket lives in the deploy region,
1060
+ // so snapshots hit a cross-region 301 PermanentRedirect. index.test.ts otherwise only
1061
+ // drives the mock/local path, so this covers agent.aws.ts by spying the S3Storage ctor.
1062
+
1063
+ import { Agent as DeployedAgent } from './index.aws.js';
1064
+ import { createDeployedSnapshotStorage } from './agent.aws.js';
1065
+ import type { SnapshotStorage, SnapshotManifest } from '@strands-agents/sdk';
1066
+ import type { S3StorageConfig } from '@strands-agents/sdk/session/s3-storage';
1067
+
1068
+ describe('deployed Agent S3Storage region (multi-region)', () => {
1069
+ // Type-safe stand-in for S3Storage that records every config it's constructed with,
1070
+ // so we assert exactly what agent.aws.ts passes in — no S3Storage/AWS SDK internals.
1071
+ function s3StorageSpy() {
1072
+ const configs: S3StorageConfig[] = [];
1073
+ class Spy implements SnapshotStorage {
1074
+ constructor(config: S3StorageConfig) {
1075
+ configs.push(config);
1076
+ }
1077
+ async saveSnapshot(): Promise<void> {}
1078
+ async loadSnapshot(): Promise<null> {
1079
+ return null;
1080
+ }
1081
+ async listSnapshotIds(): Promise<string[]> {
1082
+ return [];
1083
+ }
1084
+ async deleteSession(): Promise<void> {}
1085
+ async loadManifest(): Promise<SnapshotManifest> {
1086
+ return { schemaVersion: '1.0', updatedAt: new Date().toISOString() };
1087
+ }
1088
+ async saveManifest(): Promise<void> {}
1089
+ }
1090
+ return { configs, Spy };
1091
+ }
1092
+
1093
+ // Capture the S3StorageConfig the deployed factory builds for a given AWS_REGION.
1094
+ function configForRegion(region: string, scopeId: string): S3StorageConfig {
1095
+ const prev = process.env.AWS_REGION;
1096
+ process.env.AWS_REGION = region;
1097
+ try {
1098
+ const { configs, Spy } = s3StorageSpy();
1099
+ const bucket = new FileBucket(new Scope(scopeId), 'sn');
1100
+ createDeployedSnapshotStorage(bucket, Spy);
1101
+ assert.strictEqual(configs.length, 1, 'S3Storage should be constructed exactly once');
1102
+ assert.strictEqual(configs[0].bucket, bucket.fullId, 'session bucket id should be passed through');
1103
+ return configs[0];
1104
+ } finally {
1105
+ if (prev === undefined) delete process.env.AWS_REGION;
1106
+ else process.env.AWS_REGION = prev;
1107
+ }
1108
+ }
1109
+
1110
+ test('constructs S3Storage with the Lambda execution region (eu-west-1)', () => {
1111
+ const config = configForRegion('eu-west-1', 'test-s3-region-euw1');
1112
+ assert.strictEqual(config.region, 'eu-west-1');
1113
+ assert.notStrictEqual(config.region, 'us-east-1');
1114
+ });
1115
+
1116
+ test('does not hard-pin us-east-1 when deployed to another region (ap-southeast-2)', () => {
1117
+ const config = configForRegion('ap-southeast-2', 'test-s3-region-apse2');
1118
+ assert.strictEqual(config.region, 'ap-southeast-2');
1119
+ assert.notStrictEqual(config.region, 'us-east-1');
1120
+ });
1121
+
1122
+ test('deployed Agent constructs on the aws-runtime path', () => {
1123
+ const agent = new DeployedAgent(new Scope('test-s3-agent'), 'r', { systemPrompt: 'test', model: { deployed: { provider: 'canned' } } });
1124
+ assert.ok(agent);
1125
+ });
1126
+ });
package/src/version.ts CHANGED
@@ -1,3 +1,3 @@
1
1
  // Auto-generated by scripts/generate-version.mjs — do not edit manually
2
2
  export const BB_NAME = 'Agent';
3
- export const BB_VERSION = '0.3.0';
3
+ export const BB_VERSION = '0.3.1';