@microsoft/agents-hosting 1.0.0-ge4831811bf → 1.0.3-g444d99f704
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/dist/package.json +2 -2
- package/dist/src/activityHandler.d.ts +0 -1
- package/dist/src/activityHandler.js +0 -1
- package/dist/src/activityHandler.js.map +1 -1
- package/dist/src/agent-client/agentResponseHandler.d.ts +6 -6
- package/dist/src/agent-client/agentResponseHandler.js +6 -6
- package/dist/src/app/adaptiveCards/adaptiveCardsActions.d.ts +12 -4
- package/dist/src/app/adaptiveCards/adaptiveCardsActions.js +5 -1
- package/dist/src/app/adaptiveCards/adaptiveCardsActions.js.map +1 -1
- package/dist/src/app/agentApplication.d.ts +24 -7
- package/dist/src/app/agentApplication.js +24 -7
- package/dist/src/app/agentApplication.js.map +1 -1
- package/dist/src/app/appRoute.d.ts +8 -4
- package/dist/src/app/attachmentDownloader.d.ts +3 -1
- package/dist/src/app/attachmentDownloader.js +3 -1
- package/dist/src/app/attachmentDownloader.js.map +1 -1
- package/dist/src/app/authorization.d.ts +26 -18
- package/dist/src/app/authorization.js +26 -18
- package/dist/src/app/authorization.js.map +1 -1
- package/dist/src/app/routeRank.d.ts +10 -3
- package/dist/src/app/routeRank.js +10 -3
- package/dist/src/app/routeRank.js.map +1 -1
- package/dist/src/app/streaming/streamingResponse.d.ts +23 -2
- package/dist/src/app/streaming/streamingResponse.js +23 -2
- package/dist/src/app/streaming/streamingResponse.js.map +1 -1
- package/dist/src/app/turnState.d.ts +69 -19
- package/dist/src/app/turnState.js +69 -19
- package/dist/src/app/turnState.js.map +1 -1
- package/dist/src/auth/authConfiguration.d.ts +16 -9
- package/dist/src/auth/authConfiguration.js +11 -6
- package/dist/src/auth/authConfiguration.js.map +1 -1
- package/dist/src/auth/jwt-middleware.js +26 -18
- package/dist/src/auth/jwt-middleware.js.map +1 -1
- package/dist/src/cards/adaptiveCard.d.ts +1 -1
- package/dist/src/headerPropagation.d.ts +18 -6
- package/dist/src/state/agentState.d.ts +23 -7
- package/dist/src/state/agentState.js +19 -7
- package/dist/src/state/agentState.js.map +1 -1
- package/dist/src/state/agentStatePropertyAccesor.d.ts +63 -41
- package/dist/src/state/agentStatePropertyAccesor.js +43 -32
- package/dist/src/state/agentStatePropertyAccesor.js.map +1 -1
- package/dist/src/storage/fileStorage.d.ts +8 -6
- package/dist/src/storage/fileStorage.js +8 -6
- package/dist/src/storage/fileStorage.js.map +1 -1
- package/dist/src/storage/memoryStorage.d.ts +12 -7
- package/dist/src/storage/memoryStorage.js +12 -7
- package/dist/src/storage/memoryStorage.js.map +1 -1
- package/dist/src/storage/storage.d.ts +18 -1
- package/dist/src/turnContext.d.ts +31 -20
- package/dist/src/turnContext.js +31 -20
- package/dist/src/turnContext.js.map +1 -1
- package/package.json +2 -2
- package/src/activityHandler.ts +0 -1
- package/src/agent-client/agentResponseHandler.ts +6 -6
- package/src/app/adaptiveCards/adaptiveCardsActions.ts +12 -4
- package/src/app/agentApplication.ts +24 -7
- package/src/app/appRoute.ts +8 -4
- package/src/app/attachmentDownloader.ts +3 -1
- package/src/app/authorization.ts +26 -18
- package/src/app/routeRank.ts +10 -3
- package/src/app/streaming/streamingResponse.ts +23 -2
- package/src/app/turnState.ts +69 -19
- package/src/auth/authConfiguration.ts +16 -9
- package/src/auth/jwt-middleware.ts +24 -17
- package/src/cards/adaptiveCard.ts +1 -1
- package/src/headerPropagation.ts +18 -6
- package/src/state/agentState.ts +23 -7
- package/src/state/agentStatePropertyAccesor.ts +63 -41
- package/src/storage/fileStorage.ts +8 -6
- package/src/storage/memoryStorage.ts +12 -7
- package/src/storage/storage.ts +18 -1
- package/src/turnContext.ts +31 -20
|
@@ -72,25 +72,32 @@ export const authorizeJWT = (authConfig: AuthConfiguration) => {
|
|
|
72
72
|
return async function (req: Request, res: Response, next: NextFunction) {
|
|
73
73
|
let failed = false
|
|
74
74
|
logger.debug('authorizing jwt')
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
const user = await verifyToken(token, authConfig)
|
|
80
|
-
logger.debug('token verified for ', user)
|
|
81
|
-
req.user = user
|
|
82
|
-
} catch (err: Error | any) {
|
|
83
|
-
failed = true
|
|
84
|
-
logger.error(err)
|
|
85
|
-
res.status(401).send({ 'jwt-auth-error': err.message })
|
|
86
|
-
}
|
|
75
|
+
if (req.method !== 'POST' && req.method !== 'GET') {
|
|
76
|
+
failed = true
|
|
77
|
+
logger.warn('Method not allowed', req.method)
|
|
78
|
+
res.status(405).send({ 'jwt-auth-error': 'Method not allowed' })
|
|
87
79
|
} else {
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
80
|
+
const authHeader = req.headers.authorization as string
|
|
81
|
+
if (authHeader) {
|
|
82
|
+
const token: string = authHeader.split(' ')[1] // Extract the token from the Bearer string
|
|
83
|
+
try {
|
|
84
|
+
const user = await verifyToken(token, authConfig)
|
|
85
|
+
logger.debug('token verified for ', user)
|
|
86
|
+
req.user = user
|
|
87
|
+
} catch (err: Error | any) {
|
|
88
|
+
failed = true
|
|
89
|
+
logger.error(err)
|
|
90
|
+
res.status(401).send({ 'jwt-auth-error': err.message })
|
|
91
|
+
}
|
|
91
92
|
} else {
|
|
92
|
-
|
|
93
|
-
|
|
93
|
+
if (!authConfig.clientId && process.env.NODE_ENV !== 'production') {
|
|
94
|
+
logger.info('using anonymous auth')
|
|
95
|
+
req.user = { name: 'anonymous' }
|
|
96
|
+
} else {
|
|
97
|
+
failed = true
|
|
98
|
+
logger.error('authorization header not found')
|
|
99
|
+
res.status(401).send({ 'jwt-auth-error': 'authorization header not found' })
|
|
100
|
+
}
|
|
94
101
|
}
|
|
95
102
|
}
|
|
96
103
|
if (!failed) {
|
package/src/headerPropagation.ts
CHANGED
|
@@ -94,36 +94,48 @@ export interface HeaderPropagationDefinition {
|
|
|
94
94
|
export interface HeaderPropagationCollection {
|
|
95
95
|
/**
|
|
96
96
|
* The collection of incoming headers from the incoming request.
|
|
97
|
-
*
|
|
97
|
+
*
|
|
98
|
+
* @remarks
|
|
99
|
+
* This collection is built based on the headers received in the request.
|
|
98
100
|
*/
|
|
99
101
|
incoming: Record<string, string>
|
|
100
102
|
/**
|
|
101
103
|
* The collection of headers that will be propagated to outgoing requests.
|
|
102
|
-
*
|
|
104
|
+
*
|
|
105
|
+
* @remarks
|
|
106
|
+
* This collection is built based on the incoming headers and the definition provided.
|
|
103
107
|
*/
|
|
104
108
|
outgoing: Record<string, string>
|
|
105
109
|
/**
|
|
106
110
|
* Propagates the incoming header value to the outgoing collection based on the header definition key.
|
|
107
111
|
* @param headers List of header keys to propagate.
|
|
108
|
-
*
|
|
112
|
+
*
|
|
113
|
+
* @remarks
|
|
114
|
+
* If the header does not exist in the incoming headers, it will be ignored.
|
|
109
115
|
*/
|
|
110
116
|
propagate(headers: string[]): void
|
|
111
117
|
/**
|
|
112
118
|
* Adds a header definition to the outgoing collection.
|
|
113
119
|
* @param headers Headers to add to the outgoing collection.
|
|
114
|
-
*
|
|
120
|
+
*
|
|
121
|
+
* @remarks
|
|
122
|
+
* If the header already exists, it will not be added.
|
|
115
123
|
*/
|
|
116
124
|
add(headers: Record<string, string>): void
|
|
117
125
|
/**
|
|
118
126
|
* Concatenates a header definition to the outgoing collection.
|
|
119
127
|
* @param headers Headers to concatenate to the outgoing collection.
|
|
120
|
-
*
|
|
128
|
+
*
|
|
129
|
+
* @remarks
|
|
130
|
+
* If the header does not exist in the incoming headers, it will be ignored. Unless the header is already present in the outgoing collection.
|
|
121
131
|
*/
|
|
122
132
|
concat(headers: Record<string, string>): void
|
|
123
133
|
/**
|
|
124
134
|
* Overrides a header definition in the outgoing collection.
|
|
125
135
|
* @param headers Headers to override in the outgoing collection.
|
|
126
|
-
*
|
|
136
|
+
*
|
|
137
|
+
* @remarks
|
|
138
|
+
* If the header does not exist in the incoming headers, it will be added to the outgoing collection.
|
|
127
139
|
*/
|
|
128
140
|
override(headers: Record<string, string>): void
|
|
129
141
|
}
|
package/src/state/agentState.ts
CHANGED
|
@@ -13,6 +13,8 @@ const logger = debug('agents:state')
|
|
|
13
13
|
|
|
14
14
|
/**
|
|
15
15
|
* Represents agent state that has been cached in the turn context.
|
|
16
|
+
*
|
|
17
|
+
* @remarks
|
|
16
18
|
* Used internally to track state changes and avoid unnecessary storage operations.
|
|
17
19
|
*/
|
|
18
20
|
export interface CachedAgentState {
|
|
@@ -28,6 +30,8 @@ export interface CachedAgentState {
|
|
|
28
30
|
|
|
29
31
|
/**
|
|
30
32
|
* Represents a custom key for storing state in a specific location.
|
|
33
|
+
*
|
|
34
|
+
* @remarks
|
|
31
35
|
* Allows state to be persisted with channel and conversation identifiers
|
|
32
36
|
* independent of the current context.
|
|
33
37
|
*/
|
|
@@ -44,7 +48,8 @@ export interface CustomKey {
|
|
|
44
48
|
}
|
|
45
49
|
|
|
46
50
|
/**
|
|
47
|
-
*
|
|
51
|
+
* Manages the state of an Agent across turns in a conversation.
|
|
52
|
+
*
|
|
48
53
|
* @remarks
|
|
49
54
|
* AgentState provides functionality to persist and retrieve state data using
|
|
50
55
|
* a storage provider. It handles caching state in the turn context for performance,
|
|
@@ -64,10 +69,12 @@ export class AgentState {
|
|
|
64
69
|
|
|
65
70
|
/**
|
|
66
71
|
* Creates a property accessor for the specified property.
|
|
67
|
-
* Property accessors provide typed access to properties within the state object.
|
|
68
72
|
*
|
|
69
73
|
* @param name The name of the property to access
|
|
70
74
|
* @returns A property accessor for the specified property
|
|
75
|
+
*
|
|
76
|
+
* @remarks
|
|
77
|
+
* Property accessors provide typed access to properties within the state object.
|
|
71
78
|
*/
|
|
72
79
|
createProperty<T = any>(name: string): AgentStatePropertyAccessor<T> {
|
|
73
80
|
const prop: AgentStatePropertyAccessor<T> = new AgentStatePropertyAccessor<T>(this, name)
|
|
@@ -76,12 +83,14 @@ export class AgentState {
|
|
|
76
83
|
|
|
77
84
|
/**
|
|
78
85
|
* Loads the state from storage into the turn context.
|
|
79
|
-
* If state is already cached in the turn context and force is not set, the cached version will be used.
|
|
80
86
|
*
|
|
81
87
|
* @param context The turn context to load state into
|
|
82
88
|
* @param force If true, forces a reload from storage even if state is cached
|
|
83
89
|
* @param customKey Optional custom storage key to use instead of the default
|
|
84
90
|
* @returns A promise that resolves to the loaded state object
|
|
91
|
+
*
|
|
92
|
+
* @remarks
|
|
93
|
+
* If state is already cached in the turn context and force is not set, the cached version will be used.
|
|
85
94
|
*/
|
|
86
95
|
public async load (context: TurnContext, force = false, customKey?: CustomKey): Promise<any> {
|
|
87
96
|
const cached: CachedAgentState = context.turnState.get(this.stateKey)
|
|
@@ -103,12 +112,14 @@ export class AgentState {
|
|
|
103
112
|
|
|
104
113
|
/**
|
|
105
114
|
* Saves the state to storage if it has changed since it was loaded.
|
|
106
|
-
* Change detection uses a hash of the state object to determine if saving is necessary.
|
|
107
115
|
*
|
|
108
116
|
* @param context The turn context containing the state to save
|
|
109
117
|
* @param force If true, forces a save to storage even if no changes are detected
|
|
110
118
|
* @param customKey Optional custom storage key to use instead of the default
|
|
111
119
|
* @returns A promise that resolves when the save operation is complete
|
|
120
|
+
*
|
|
121
|
+
* @remarks
|
|
122
|
+
* Change detection uses a hash of the state object to determine if saving is necessary.
|
|
112
123
|
*/
|
|
113
124
|
public async saveChanges (context: TurnContext, force = false, customKey?: CustomKey): Promise<void> {
|
|
114
125
|
let cached: CachedAgentState = context.turnState.get(this.stateKey)
|
|
@@ -151,11 +162,14 @@ export class AgentState {
|
|
|
151
162
|
|
|
152
163
|
/**
|
|
153
164
|
* Clears the state by setting it to an empty object in the turn context.
|
|
154
|
-
* Note: This does not remove the state from storage, it only clears the in-memory representation.
|
|
155
|
-
* Call saveChanges() after this to persist the empty state to storage.
|
|
156
165
|
*
|
|
157
166
|
* @param context The turn context containing the state to clear
|
|
158
167
|
* @returns A promise that resolves when the clear operation is complete
|
|
168
|
+
*
|
|
169
|
+
* @remarks
|
|
170
|
+
* This does not remove the state from storage, it only clears the in-memory representation.
|
|
171
|
+
* Call saveChanges() after this to persist the empty state to storage.
|
|
172
|
+
*
|
|
159
173
|
*/
|
|
160
174
|
public async clear (context: TurnContext): Promise<void> {
|
|
161
175
|
const emptyObjectToForceSave = { state: {}, hash: '' }
|
|
@@ -193,10 +207,12 @@ export class AgentState {
|
|
|
193
207
|
|
|
194
208
|
/**
|
|
195
209
|
* Calculates a hash for the specified state object to detect changes.
|
|
196
|
-
* The eTag property is excluded from the hash calculation.
|
|
197
210
|
*
|
|
198
211
|
* @param item The state object to calculate the hash for
|
|
199
212
|
* @returns A string hash representing the state
|
|
213
|
+
*
|
|
214
|
+
* @remarks
|
|
215
|
+
* The eTag property is excluded from the hash calculation.
|
|
200
216
|
* @private
|
|
201
217
|
*/
|
|
202
218
|
private readonly calculateChangeHash = (item: StoreItem): string => {
|
|
@@ -9,29 +9,36 @@ import { AgentState, CustomKey } from './agentState'
|
|
|
9
9
|
/**
|
|
10
10
|
* Interface for accessing a property in state storage with type safety.
|
|
11
11
|
*
|
|
12
|
-
*
|
|
12
|
+
* @typeParam T The type of the property being accessed
|
|
13
|
+
*
|
|
14
|
+
* @remarks
|
|
15
|
+
* This interface defines standard methods for working with persisted state properties,
|
|
13
16
|
* allowing property access with strong typing to reduce errors when working with
|
|
14
17
|
* complex state objects.
|
|
15
18
|
*
|
|
16
|
-
* @typeParam T The type of the property being accessed
|
|
17
19
|
*/
|
|
18
20
|
export interface StatePropertyAccessor<T = any> {
|
|
19
21
|
/**
|
|
20
22
|
* Deletes the persisted property from its backing storage object.
|
|
21
23
|
*
|
|
24
|
+
* @param context Context for the current turn of conversation with the user.
|
|
25
|
+
*
|
|
22
26
|
* @remarks
|
|
23
27
|
* The properties backing storage object SHOULD be loaded into memory on first access.
|
|
24
28
|
*
|
|
25
|
-
*
|
|
29
|
+
* @example
|
|
30
|
+
* ```javascript
|
|
26
31
|
* await myProperty.delete(context);
|
|
27
32
|
* ```
|
|
28
|
-
*
|
|
33
|
+
*
|
|
29
34
|
*/
|
|
30
35
|
delete(context: TurnContext): Promise<void>;
|
|
31
36
|
|
|
32
37
|
/**
|
|
33
38
|
* Reads a persisted property from its backing storage object.
|
|
34
39
|
*
|
|
40
|
+
* @param context Context for the current turn of conversation with the user.
|
|
41
|
+
*
|
|
35
42
|
* @remarks
|
|
36
43
|
* The properties backing storage object SHOULD be loaded into memory on first access.
|
|
37
44
|
*
|
|
@@ -39,10 +46,11 @@ export interface StatePropertyAccessor<T = any> {
|
|
|
39
46
|
* specified, a clone of the `defaultValue` SHOULD be copied to the storage object. If a
|
|
40
47
|
* `defaultValue` has not been specified then a value of `undefined` SHOULD be returned.
|
|
41
48
|
*
|
|
42
|
-
*
|
|
49
|
+
* @example
|
|
50
|
+
* ```javascript
|
|
43
51
|
* const value = await myProperty.get(context, { count: 0 });
|
|
44
52
|
* ```
|
|
45
|
-
*
|
|
53
|
+
*
|
|
46
54
|
*/
|
|
47
55
|
get(context: TurnContext): Promise<T | undefined>;
|
|
48
56
|
|
|
@@ -57,26 +65,31 @@ export interface StatePropertyAccessor<T = any> {
|
|
|
57
65
|
/**
|
|
58
66
|
* Assigns a new value to the properties backing storage object.
|
|
59
67
|
*
|
|
68
|
+
* @param context Context for the current turn of conversation with the user.
|
|
69
|
+
* @param value Value to assign.
|
|
70
|
+
*
|
|
60
71
|
* @remarks
|
|
61
72
|
* The properties backing storage object SHOULD be loaded into memory on first access.
|
|
62
73
|
*
|
|
63
74
|
* Depending on the state systems implementation, an additional step may be required to
|
|
64
75
|
* persist the actual changes to disk.
|
|
65
76
|
*
|
|
66
|
-
*
|
|
77
|
+
* @example
|
|
78
|
+
* ```javascript
|
|
67
79
|
* await myProperty.set(context, value);
|
|
68
80
|
* ```
|
|
69
|
-
*
|
|
70
|
-
* @param value Value to assign.
|
|
81
|
+
*
|
|
71
82
|
*/
|
|
72
83
|
set(context: TurnContext, value: T): Promise<void>;
|
|
73
84
|
}
|
|
74
85
|
|
|
75
86
|
/**
|
|
76
|
-
*
|
|
87
|
+
* Provides typed access to an Agent state property with automatic state loading and persistence management.
|
|
88
|
+
*
|
|
89
|
+
* @typeParam T The type of the property being accessed. Can be any serializable type.
|
|
77
90
|
*
|
|
78
91
|
* @remarks
|
|
79
|
-
* AgentStatePropertyAccessor simplifies working with persisted state by abstracting
|
|
92
|
+
* `AgentStatePropertyAccessor` simplifies working with persisted state by abstracting
|
|
80
93
|
* the complexity of loading state from storage and manipulating specific properties.
|
|
81
94
|
* It provides a type-safe interface for state management with automatic handling of:
|
|
82
95
|
*
|
|
@@ -88,6 +101,11 @@ export interface StatePropertyAccessor<T = any> {
|
|
|
88
101
|
*
|
|
89
102
|
* ### Key Features
|
|
90
103
|
*
|
|
104
|
+
* Key features of `AgentStatePropertyAccessor` include:
|
|
105
|
+
* - [Type Safety](#type-safety)
|
|
106
|
+
* - [Automatic Default Value Handling](#automatic-default-value-handling)
|
|
107
|
+
* - [Explicit Persistence Control](#explicit-persistence-control)
|
|
108
|
+
*
|
|
91
109
|
* #### Type Safety
|
|
92
110
|
* The accessor provides compile-time type checking when using TypeScript:
|
|
93
111
|
* ```typescript
|
|
@@ -178,8 +196,6 @@ export interface StatePropertyAccessor<T = any> {
|
|
|
178
196
|
* - **Persistence**: Always call `state.saveChanges(context)` to persist changes to storage.
|
|
179
197
|
* - **Deep Cloning**: Default values are deep cloned using JSON serialization, which may not work with complex objects containing functions or circular references.
|
|
180
198
|
*
|
|
181
|
-
* @typeParam T The type of the property being accessed. Can be any serializable type.
|
|
182
|
-
*
|
|
183
199
|
* @see {@link AgentState.createProperty} for creating property accessors
|
|
184
200
|
* @see {@link StatePropertyAccessor} for the interface definition
|
|
185
201
|
*/
|
|
@@ -187,13 +203,13 @@ export class AgentStatePropertyAccessor<T = any> implements StatePropertyAccesso
|
|
|
187
203
|
/**
|
|
188
204
|
* Creates a new instance of AgentStatePropertyAccessor.
|
|
189
205
|
*
|
|
206
|
+
* @param state The agent state object that manages the backing storage for this property
|
|
207
|
+
* @param name The unique name of the property within the state object. This name is used as the key in the state storage.
|
|
208
|
+
*
|
|
190
209
|
* @remarks
|
|
191
210
|
* This constructor is typically not called directly. Instead, use {@link AgentState.createProperty}
|
|
192
211
|
* to create property accessors, which ensures proper integration with the state management system.
|
|
193
212
|
*
|
|
194
|
-
* @param state The agent state object that manages the backing storage for this property
|
|
195
|
-
* @param name The unique name of the property within the state object. This name is used as the key in the state storage.
|
|
196
|
-
*
|
|
197
213
|
* @example
|
|
198
214
|
* ```typescript
|
|
199
215
|
* // Recommended way - use AgentState.createProperty
|
|
@@ -202,11 +218,18 @@ export class AgentStatePropertyAccessor<T = any> implements StatePropertyAccesso
|
|
|
202
218
|
* // Direct construction (not recommended)
|
|
203
219
|
* const accessor = new AgentStatePropertyAccessor<UserProfile>(userState, "userProfile");
|
|
204
220
|
* ```
|
|
221
|
+
*
|
|
205
222
|
*/
|
|
206
223
|
constructor (protected readonly state: AgentState, public readonly name: string) { }
|
|
207
224
|
|
|
208
225
|
/**
|
|
209
|
-
*
|
|
226
|
+
* Deletes the property from the state storage.
|
|
227
|
+
*
|
|
228
|
+
* @param context The turn context for the current conversation turn
|
|
229
|
+
* @param customKey Optional custom key for accessing state in a specific storage location.
|
|
230
|
+
* Useful for multi-tenant scenarios or when state needs to be partitioned.
|
|
231
|
+
* @returns A promise that resolves when the delete operation is complete
|
|
232
|
+
*
|
|
210
233
|
* @remarks
|
|
211
234
|
* This operation removes the property from the in-memory state object but does not
|
|
212
235
|
* automatically persist the change to the underlying storage. You must call
|
|
@@ -216,13 +239,7 @@ export class AgentStatePropertyAccessor<T = any> implements StatePropertyAccesso
|
|
|
216
239
|
* - The deletion only affects the in-memory state until `saveChanges()` is called
|
|
217
240
|
* - After deletion, subsequent `get()` calls will return `undefined` (or the default value if provided)
|
|
218
241
|
*
|
|
219
|
-
* @
|
|
220
|
-
* @param customKey Optional custom key for accessing state in a specific storage location.
|
|
221
|
-
* Useful for multi-tenant scenarios or when state needs to be partitioned.
|
|
222
|
-
*
|
|
223
|
-
* @returns A promise that resolves when the delete operation is complete
|
|
224
|
-
*
|
|
225
|
-
* @example
|
|
242
|
+
* @example Basic usage
|
|
226
243
|
* ```typescript
|
|
227
244
|
* const userSettings = userState.createProperty<UserSettings>("settings");
|
|
228
245
|
*
|
|
@@ -242,6 +259,7 @@ export class AgentStatePropertyAccessor<T = any> implements StatePropertyAccesso
|
|
|
242
259
|
* await userSettings.delete(context, tenantKey);
|
|
243
260
|
* await userState.saveChanges(context);
|
|
244
261
|
* ```
|
|
262
|
+
*
|
|
245
263
|
*/
|
|
246
264
|
async delete (context: TurnContext, customKey?: CustomKey): Promise<void> {
|
|
247
265
|
const obj: any = await this.state.load(context, false, customKey)
|
|
@@ -251,7 +269,16 @@ export class AgentStatePropertyAccessor<T = any> implements StatePropertyAccesso
|
|
|
251
269
|
}
|
|
252
270
|
|
|
253
271
|
/**
|
|
254
|
-
*
|
|
272
|
+
* Retrieves the value of the property from state storage.
|
|
273
|
+
*
|
|
274
|
+
* @param context The turn context for the current conversation turn
|
|
275
|
+
* @param defaultValue Optional default value to use if the property doesn't exist.
|
|
276
|
+
* When provided, this value is deep cloned and stored in state.
|
|
277
|
+
* @param customKey Optional custom key for accessing state in a specific storage location.
|
|
278
|
+
* Useful for multi-tenant scenarios or when state needs to be partitioned.
|
|
279
|
+
*
|
|
280
|
+
* @returns A promise that resolves to the property value, the cloned default value, or `undefined`
|
|
281
|
+
*
|
|
255
282
|
* @remarks
|
|
256
283
|
* This method provides intelligent default value handling:
|
|
257
284
|
* - If the property exists, its value is returned
|
|
@@ -268,14 +295,6 @@ export class AgentStatePropertyAccessor<T = any> implements StatePropertyAccesso
|
|
|
268
295
|
* **Performance**: The first access loads state from storage; subsequent accesses use
|
|
269
296
|
* the in-memory cached version until the context is disposed.
|
|
270
297
|
*
|
|
271
|
-
* @param context The turn context for the current conversation turn
|
|
272
|
-
* @param defaultValue Optional default value to use if the property doesn't exist.
|
|
273
|
-
* When provided, this value is deep cloned and stored in state.
|
|
274
|
-
* @param customKey Optional custom key for accessing state in a specific storage location.
|
|
275
|
-
* Useful for multi-tenant scenarios or when state needs to be partitioned.
|
|
276
|
-
*
|
|
277
|
-
* @returns A promise that resolves to the property value, the cloned default value, or `undefined`
|
|
278
|
-
*
|
|
279
298
|
* @example Basic usage
|
|
280
299
|
* ```typescript
|
|
281
300
|
* const counterProperty = userState.createProperty<number>("counter");
|
|
@@ -314,6 +333,7 @@ export class AgentStatePropertyAccessor<T = any> implements StatePropertyAccesso
|
|
|
314
333
|
* const tenantKey = { key: `tenant_${tenantId}` };
|
|
315
334
|
* const tenantData = await dataProperty.get(context, defaultData, tenantKey);
|
|
316
335
|
* ```
|
|
336
|
+
*
|
|
317
337
|
*/
|
|
318
338
|
async get (context: TurnContext, defaultValue?: T, customKey?: CustomKey): Promise<T> {
|
|
319
339
|
const obj: any = await this.state.load(context, false, customKey)
|
|
@@ -329,7 +349,15 @@ export class AgentStatePropertyAccessor<T = any> implements StatePropertyAccesso
|
|
|
329
349
|
}
|
|
330
350
|
|
|
331
351
|
/**
|
|
332
|
-
*
|
|
352
|
+
* Sets the value of the property in state storage.
|
|
353
|
+
*
|
|
354
|
+
* @param context The turn context for the current conversation turn
|
|
355
|
+
* @param value The value to assign to the property. Can be any serializable value.
|
|
356
|
+
* @param customKey Optional custom key for accessing state in a specific storage location.
|
|
357
|
+
* Useful for multi-tenant scenarios or when state needs to be partitioned.
|
|
358
|
+
*
|
|
359
|
+
* @returns A promise that resolves when the set operation is complete
|
|
360
|
+
*
|
|
333
361
|
* @remarks
|
|
334
362
|
* This operation updates the property in the in-memory state object but does not
|
|
335
363
|
* automatically persist the change to the underlying storage. You must call
|
|
@@ -346,13 +374,6 @@ export class AgentStatePropertyAccessor<T = any> implements StatePropertyAccesso
|
|
|
346
374
|
* **Type Safety**: When using TypeScript, the value must match the property's
|
|
347
375
|
* declared type parameter.
|
|
348
376
|
*
|
|
349
|
-
* @param context The turn context for the current conversation turn
|
|
350
|
-
* @param value The value to assign to the property. Can be any serializable value.
|
|
351
|
-
* @param customKey Optional custom key for accessing state in a specific storage location.
|
|
352
|
-
* Useful for multi-tenant scenarios or when state needs to be partitioned.
|
|
353
|
-
*
|
|
354
|
-
* @returns A promise that resolves when the set operation is complete
|
|
355
|
-
*
|
|
356
377
|
* @example Basic usage
|
|
357
378
|
* ```typescript
|
|
358
379
|
* const counterProperty = userState.createProperty<number>("counter");
|
|
@@ -394,6 +415,7 @@ export class AgentStatePropertyAccessor<T = any> implements StatePropertyAccesso
|
|
|
394
415
|
* await dataProperty.set(context, updatedData, tenantKey);
|
|
395
416
|
* await userState.saveChanges(context);
|
|
396
417
|
* ```
|
|
418
|
+
*
|
|
397
419
|
*/
|
|
398
420
|
async set (context: TurnContext, value: T, customKey?: CustomKey): Promise<void> {
|
|
399
421
|
const obj: any = await this.state.load(context, false, customKey)
|
|
@@ -8,7 +8,7 @@ import fs from 'fs'
|
|
|
8
8
|
import { Storage, StoreItem } from './storage'
|
|
9
9
|
|
|
10
10
|
/**
|
|
11
|
-
*
|
|
11
|
+
* A file-based storage implementation that persists data to the local filesystem.
|
|
12
12
|
*
|
|
13
13
|
* @remarks
|
|
14
14
|
* FileStorage stores all data in a single JSON file named 'state.json' within a specified folder.
|
|
@@ -47,7 +47,6 @@ import { Storage, StoreItem } from './storage'
|
|
|
47
47
|
* await storage.delete(['conversation456']);
|
|
48
48
|
* ```
|
|
49
49
|
*
|
|
50
|
-
|
|
51
50
|
*/
|
|
52
51
|
export class FileStorage implements Storage {
|
|
53
52
|
private _folder: string
|
|
@@ -57,6 +56,7 @@ export class FileStorage implements Storage {
|
|
|
57
56
|
* Creates a new FileStorage instance that stores data in the specified folder.
|
|
58
57
|
*
|
|
59
58
|
* @param folder The absolute or relative path to the folder where the state.json file will be stored
|
|
59
|
+
* @throws May throw filesystem errors if the folder cannot be created or accessed
|
|
60
60
|
*
|
|
61
61
|
* @remarks
|
|
62
62
|
* The constructor performs the following initialization steps:
|
|
@@ -64,7 +64,6 @@ export class FileStorage implements Storage {
|
|
|
64
64
|
* 2. Creates an empty state.json file if it doesn't exist
|
|
65
65
|
* 3. Loads existing data from state.json into memory for fast access
|
|
66
66
|
*
|
|
67
|
-
* @throws May throw filesystem errors if the folder cannot be created or accessed
|
|
68
67
|
*/
|
|
69
68
|
constructor (folder: string) {
|
|
70
69
|
this._folder = folder
|
|
@@ -83,13 +82,13 @@ export class FileStorage implements Storage {
|
|
|
83
82
|
*
|
|
84
83
|
* @param keys Array of keys to read from storage
|
|
85
84
|
* @returns Promise resolving to an object containing the requested items (keys that don't exist are omitted)
|
|
86
|
-
*
|
|
87
85
|
* @throws ReferenceError if keys array is empty or undefined
|
|
88
86
|
*
|
|
89
87
|
* @remarks
|
|
90
88
|
* This method reads from the in-memory cache that was loaded during construction,
|
|
91
89
|
* making it very fast but potentially returning stale data if the file was
|
|
92
90
|
* modified by external processes.
|
|
91
|
+
*
|
|
93
92
|
*/
|
|
94
93
|
read (keys: string[]) : Promise<StoreItem> {
|
|
95
94
|
return new Promise((resolve, reject) => {
|
|
@@ -119,8 +118,10 @@ export class FileStorage implements Storage {
|
|
|
119
118
|
* to the state.json file. The file is written with pretty-printing (2-space indentation)
|
|
120
119
|
* for better readability during development and debugging.
|
|
121
120
|
*
|
|
122
|
-
*
|
|
123
|
-
*
|
|
121
|
+
* > [!NOTE]
|
|
122
|
+
* > This implementation does not support eTag-based optimistic concurrency control.
|
|
123
|
+
* > Any eTag values in the changes object are ignored.
|
|
124
|
+
*
|
|
124
125
|
*/
|
|
125
126
|
write (changes: StoreItem) : Promise<void> {
|
|
126
127
|
const keys = Object.keys(changes)
|
|
@@ -143,6 +144,7 @@ export class FileStorage implements Storage {
|
|
|
143
144
|
* This method removes the specified keys from both the in-memory cache
|
|
144
145
|
* and writes the updated state to the state.json file. Keys that don't
|
|
145
146
|
* exist in storage are silently ignored.
|
|
147
|
+
*
|
|
146
148
|
*/
|
|
147
149
|
delete (keys: string[]) : Promise<void> {
|
|
148
150
|
return new Promise((resolve, reject) => {
|
|
@@ -39,10 +39,12 @@ export class MemoryStorage implements Storage {
|
|
|
39
39
|
/**
|
|
40
40
|
* Gets a single shared instance of the MemoryStorage class.
|
|
41
41
|
*
|
|
42
|
+
* @returns The singleton instance of MemoryStorage
|
|
43
|
+
*
|
|
44
|
+
* @remarks
|
|
42
45
|
* Using this method ensures that the same storage instance is used across
|
|
43
46
|
* the application, allowing for shared state without passing references.
|
|
44
47
|
*
|
|
45
|
-
* @returns The singleton instance of MemoryStorage
|
|
46
48
|
*/
|
|
47
49
|
static getSingleInstance (): MemoryStorage {
|
|
48
50
|
if (!MemoryStorage.instance) {
|
|
@@ -78,14 +80,15 @@ export class MemoryStorage implements Storage {
|
|
|
78
80
|
/**
|
|
79
81
|
* Writes storage items to memory.
|
|
80
82
|
*
|
|
83
|
+
* @param changes The items to write, indexed by key
|
|
84
|
+
* @returns A promise that resolves when the write operation is complete
|
|
85
|
+
* @throws Will throw an error if changes are not provided or if there's an eTag conflict
|
|
86
|
+
*
|
|
87
|
+
* @remarks
|
|
81
88
|
* This method supports optimistic concurrency control through eTags.
|
|
82
89
|
* If an item has an eTag, it will only be updated if the existing item
|
|
83
90
|
* has the same eTag. If an item has an eTag of '*' or no eTag, it will
|
|
84
91
|
* always be written regardless of the current state.
|
|
85
|
-
*
|
|
86
|
-
* @param changes The items to write, indexed by key
|
|
87
|
-
* @returns A promise that resolves when the write operation is complete
|
|
88
|
-
* @throws Will throw an error if changes are not provided or if there's an eTag conflict
|
|
89
92
|
*/
|
|
90
93
|
async write (changes: StoreItem): Promise<void> {
|
|
91
94
|
if (!changes || changes.length === 0) {
|
|
@@ -124,13 +127,15 @@ export class MemoryStorage implements Storage {
|
|
|
124
127
|
/**
|
|
125
128
|
* Saves an item to memory with a new eTag.
|
|
126
129
|
*
|
|
130
|
+
* @param key The key of the item to save
|
|
131
|
+
* @param item The item to save
|
|
132
|
+
*
|
|
133
|
+
* @remarks
|
|
127
134
|
* This private method handles the details of:
|
|
128
135
|
* - Creating a clone of the item to prevent modification of the original
|
|
129
136
|
* - Generating a new eTag for optimistic concurrency control
|
|
130
137
|
* - Converting the item to a JSON string for storage
|
|
131
138
|
*
|
|
132
|
-
* @param key The key of the item to save
|
|
133
|
-
* @param item The item to save
|
|
134
139
|
* @private
|
|
135
140
|
*/
|
|
136
141
|
private saveItem (key: string, item: unknown): void {
|
package/src/storage/storage.ts
CHANGED
|
@@ -7,41 +7,58 @@ import { TurnContext } from '../turnContext'
|
|
|
7
7
|
|
|
8
8
|
/**
|
|
9
9
|
* Represents an item to be stored in a storage provider.
|
|
10
|
+
*
|
|
11
|
+
* @remarks
|
|
10
12
|
* Each item can contain arbitrary data along with an optional eTag for optimistic concurrency control.
|
|
11
13
|
*/
|
|
12
14
|
export interface StoreItem {
|
|
13
15
|
/**
|
|
14
16
|
* Optional eTag used for optimistic concurrency control.
|
|
17
|
+
*
|
|
18
|
+
* @remarks
|
|
15
19
|
* When set to '*', it indicates that the write should proceed regardless of existing data.
|
|
16
20
|
* When comparing eTags, exact string matching is used to determine if data has changed.
|
|
21
|
+
*
|
|
17
22
|
*/
|
|
18
23
|
eTag?: string;
|
|
19
24
|
|
|
20
25
|
/**
|
|
21
26
|
* Additional properties can be stored in the item.
|
|
27
|
+
*
|
|
28
|
+
* @remarks
|
|
22
29
|
* Each storage provider may have specific requirements or limitations on property names and values.
|
|
30
|
+
*
|
|
23
31
|
*/
|
|
24
32
|
[key: string]: any;
|
|
25
33
|
}
|
|
26
34
|
|
|
27
35
|
/**
|
|
28
36
|
* Represents a collection of store items indexed by key.
|
|
37
|
+
*
|
|
38
|
+
* @remarks
|
|
29
39
|
* Used as the return type for storage read operations.
|
|
40
|
+
*
|
|
30
41
|
*/
|
|
31
42
|
export interface StoreItems {
|
|
32
43
|
/**
|
|
33
44
|
* Keys are the storage item identifiers, and values are the stored items.
|
|
45
|
+
*
|
|
46
|
+
* @remarks
|
|
34
47
|
* If a requested key is not found during a read operation, it will not appear in this collection.
|
|
48
|
+
*
|
|
35
49
|
*/
|
|
36
50
|
[key: string]: any;
|
|
37
51
|
}
|
|
38
52
|
|
|
39
53
|
/**
|
|
40
54
|
* A factory function to generate storage keys based on the conversation context.
|
|
41
|
-
* Allows different storage strategies based on the conversation state.
|
|
42
55
|
*
|
|
43
56
|
* @param context The TurnContext for the current turn of conversation
|
|
44
57
|
* @returns A string key for storage that uniquely identifies where to store the data
|
|
58
|
+
*
|
|
59
|
+
* @remarks
|
|
60
|
+
* Allows different storage strategies based on the conversation state.
|
|
61
|
+
*
|
|
45
62
|
*/
|
|
46
63
|
export type StorageKeyFactory = (context: TurnContext) => string | Promise<string>
|
|
47
64
|
|