Skip to content

Commit 9ae5e89

Browse files
committed
fix: TUI UX bug, linter failures and missing telemetry
1 parent 522851d commit 9ae5e89

7 files changed

Lines changed: 66 additions & 38 deletions

File tree

src/cli/aws/policy-generation.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import {
66
StartPolicyGenerationCommand,
77
waitUntilPolicyGenerationCompleted,
88
} from '@aws-sdk/client-bedrock-agentcore-control';
9-
import { WaiterState } from '@smithy/util-waiter';
9+
import { WaiterState } from '@smithy/core/client';
1010

1111
export interface StartPolicyGenerationOptions {
1212
policyEngineId: string;

src/cli/commands/shared/indexed-key-parser.ts

Lines changed: 29 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,36 @@
11
import type { IndexedKey, IndexedKeyType } from '../../../schema';
2-
import { IndexedKeyTypeSchema } from '../../../schema';
2+
import {
3+
INDEXED_KEY_NAME_PATTERN,
4+
INDEXED_KEY_NAME_PATTERN_MESSAGE,
5+
IndexedKeyTypeSchema,
6+
MAX_INDEXED_KEYS,
7+
MAX_INDEXED_KEY_NAME_LENGTH,
8+
} from '../../../schema';
39

4-
export const INDEXED_KEY_NAME_PATTERN = /^[a-zA-Z0-9\s._:/=+@-]+$/;
5-
export const MAX_INDEXED_KEYS = 10;
6-
export const MAX_KEY_NAME_LENGTH = 128;
10+
export { INDEXED_KEY_NAME_PATTERN, MAX_INDEXED_KEYS };
11+
export const MAX_KEY_NAME_LENGTH = MAX_INDEXED_KEY_NAME_LENGTH;
712
export const VALID_INDEXED_KEY_TYPES: readonly IndexedKeyType[] = ['STRING', 'STRINGLIST', 'NUMBER'];
813

14+
/**
15+
* Validate an indexed key name. Returns `true` when valid, or an error message.
16+
* Shared between the schema-side regex (via constants) and TUI inline validation.
17+
*/
18+
export function validateIndexedKeyName(value: string, existingNames: readonly string[] = []): true | string {
19+
if (!INDEXED_KEY_NAME_PATTERN.test(value)) {
20+
return INDEXED_KEY_NAME_PATTERN_MESSAGE;
21+
}
22+
if (value.trim().length === 0) {
23+
return 'Key cannot be only whitespace';
24+
}
25+
if (value.length > MAX_INDEXED_KEY_NAME_LENGTH) {
26+
return `Maximum ${MAX_INDEXED_KEY_NAME_LENGTH} characters`;
27+
}
28+
if (existingNames.includes(value)) {
29+
return 'Key already defined';
30+
}
31+
return true;
32+
}
33+
934
export interface IndexedKeyParseError {
1035
ok: false;
1136
error: string;

src/cli/primitives/MemoryPrimitive.tsx

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -250,12 +250,15 @@ export class MemoryPrimitive extends BasePrimitive<AddMemoryOptions, RemovableMe
250250
.split(',')
251251
.map(s => s.trim().toUpperCase())
252252
.filter(Boolean);
253+
const indexedKeyCount = cliOptions.indexedKey?.length ?? 0;
253254
return {
254255
strategy_count: strategyList.length,
255256
strategy_semantic: strategyList.includes('SEMANTIC'),
256257
strategy_summarization: strategyList.includes('SUMMARIZATION'),
257258
strategy_user_preference: strategyList.includes('USER_PREFERENCE'),
258259
strategy_episodic: strategyList.includes('EPISODIC'),
260+
indexed_key_count: indexedKeyCount,
261+
has_indexed_keys: indexedKeyCount > 0,
259262
};
260263
});
261264
} else {

src/cli/telemetry/schemas/command-run.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,8 @@ const AddMemoryAttrs = safeSchema({
6565
strategy_summarization: z.boolean(),
6666
strategy_user_preference: z.boolean(),
6767
strategy_episodic: z.boolean(),
68+
indexed_key_count: Count,
69+
has_indexed_keys: z.boolean(),
6870
});
6971

7072
const AddCredentialAttrs = safeSchema({ credential_type: CredentialType });

src/cli/tui/hooks/useCreateMemory.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ export function useCreateMemory() {
2828
const strategiesStr = config.strategies.map(s => s.type).join(',');
2929
const strategyList = strategiesStr ? strategiesStr.split(',').map(s => s.trim().toUpperCase()) : [];
3030
const indexedKey = config.indexedKeys?.map(k => `${k.key}:${k.type}`);
31+
const indexedKeyCount = config.indexedKeys?.length ?? 0;
3132

3233
const addResult = await withCommandRunTelemetry(
3334
'add.memory',
@@ -37,6 +38,8 @@ export function useCreateMemory() {
3738
strategy_summarization: strategyList.includes('SUMMARIZATION'),
3839
strategy_user_preference: strategyList.includes('USER_PREFERENCE'),
3940
strategy_episodic: strategyList.includes('EPISODIC'),
41+
indexed_key_count: indexedKeyCount,
42+
has_indexed_keys: indexedKeyCount > 0,
4043
},
4144
() =>
4245
memoryPrimitive.add({

src/cli/tui/screens/memory/AddMemoryScreen.tsx

Lines changed: 19 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import type { IndexedKeyType, MemoryStrategyType } from '../../../../schema';
22
import { AgentNameSchema, StreamContentLevelSchema } from '../../../../schema';
33
import { ARN_VALIDATION_MESSAGE, isValidArn } from '../../../commands/shared/arn-utils';
4+
import { validateIndexedKeyName } from '../../../commands/shared/indexed-key-parser';
45
import {
56
ConfirmReview,
67
Panel,
@@ -24,7 +25,7 @@ import {
2425
} from './types';
2526
import { useAddMemoryWizard } from './useAddMemoryWizard';
2627
import { Box, Text } from 'ink';
27-
import React, { useCallback, useMemo, useState } from 'react';
28+
import React, { useCallback, useEffect, useMemo, useState } from 'react';
2829

2930
interface AddMemoryScreenProps {
3031
onComplete: (config: AddMemoryConfig) => void;
@@ -72,16 +73,20 @@ export function AddMemoryScreen({ onComplete, onExit, existingMemoryNames }: Add
7273
const isStrategiesStep = wizard.step === 'strategies';
7374
const isIndexedKeysStep = wizard.step === 'indexedKeys';
7475

75-
if (
76-
isIndexedKeysStep &&
77-
indexedKeysSubStep === 'prompt' &&
78-
collectedKeys.length === 0 &&
79-
wizard.config.indexedKeys &&
80-
wizard.config.indexedKeys.length > 0
81-
) {
82-
setCollectedKeys(wizard.config.indexedKeys);
83-
setIndexedKeysSubStep('addAnother');
84-
}
76+
/* eslint-disable react-hooks/set-state-in-effect */
77+
useEffect(() => {
78+
if (
79+
isIndexedKeysStep &&
80+
indexedKeysSubStep === 'prompt' &&
81+
collectedKeys.length === 0 &&
82+
wizard.config.indexedKeys &&
83+
wizard.config.indexedKeys.length > 0
84+
) {
85+
setCollectedKeys(wizard.config.indexedKeys);
86+
setIndexedKeysSubStep('addAnother');
87+
}
88+
}, [isIndexedKeysStep, indexedKeysSubStep, collectedKeys.length, wizard.config.indexedKeys]);
89+
/* eslint-enable react-hooks/set-state-in-effect */
8590

8691
const isStreamingStep = wizard.step === 'streaming';
8792
const isStreamArnStep = wizard.step === 'streamArn';
@@ -305,31 +310,18 @@ export function AddMemoryScreen({ onComplete, onExit, existingMemoryNames }: Add
305310
<TextInput
306311
key={`keyName-${collectedKeys.length}`}
307312
prompt="Metadata key name"
308-
initialValue=""
313+
initialValue={pendingKeyName}
309314
onSubmit={handleKeyNameSubmit}
310315
onCancel={() => {
316+
setPendingKeyName('');
311317
if (collectedKeys.length > 0) {
312318
setIndexedKeysSubStep('addAnother');
313319
} else {
314320
wizard.clearIndexedKeys();
315321
setIndexedKeysSubStep('prompt');
316322
}
317323
}}
318-
customValidation={value => {
319-
if (!/^[a-zA-Z0-9\s._:/=+@-]+$/.test(value)) {
320-
return 'Must contain only alphanumeric characters, whitespace, or the symbols . _ : / = + @ -';
321-
}
322-
if (value.trim().length === 0) {
323-
return 'Key cannot be only whitespace';
324-
}
325-
if (value.length > 128) {
326-
return 'Maximum 128 characters';
327-
}
328-
if (existingKeyNames.includes(value)) {
329-
return 'Key already defined';
330-
}
331-
return true;
332-
}}
324+
customValidation={value => validateIndexedKeyName(value, existingKeyNames)}
333325
/>
334326
</Box>
335327
)}

src/schema/schemas/agentcore-project.ts

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -162,15 +162,18 @@ export type IndexedKeyType = z.infer<typeof IndexedKeyTypeSchema>;
162162
* Note: indexed keys are append-only on the AWS service side — once added to a Memory,
163163
* a key cannot be removed. Reducing the array on update will fail at deploy time.
164164
*/
165+
export const INDEXED_KEY_NAME_PATTERN = /^[a-zA-Z0-9\s._:/=+@-]+$/;
166+
export const INDEXED_KEY_NAME_PATTERN_MESSAGE =
167+
'Must contain only alphanumeric characters, whitespace, or the symbols . _ : / = + @ -';
168+
export const MAX_INDEXED_KEY_NAME_LENGTH = 128;
169+
export const MAX_INDEXED_KEYS = 10;
170+
165171
export const IndexedKeySchema = z.object({
166172
key: z
167173
.string()
168174
.min(1)
169-
.max(128)
170-
.regex(
171-
/^[a-zA-Z0-9\s._:/=+@-]+$/,
172-
'Must contain only alphanumeric characters, whitespace, or the symbols . _ : / = + @ -'
173-
)
175+
.max(MAX_INDEXED_KEY_NAME_LENGTH)
176+
.regex(INDEXED_KEY_NAME_PATTERN, INDEXED_KEY_NAME_PATTERN_MESSAGE)
174177
.refine(s => s.trim().length > 0, 'Key cannot be only whitespace'),
175178
type: IndexedKeyTypeSchema,
176179
});
@@ -193,7 +196,7 @@ export const MemorySchema = z
193196
),
194197
indexedKeys: z
195198
.array(IndexedKeySchema)
196-
.max(10)
199+
.max(MAX_INDEXED_KEYS)
197200
.superRefine(
198201
uniqueBy(
199202
entry => entry.key,

0 commit comments

Comments
 (0)