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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 27 additions & 0 deletions apps/web/src/hooks/useAcpSlashCommands.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,33 @@ describe('useAcpSlashCommands', () => {
expect(container?.textContent).toBe('');
});

it('falls back to an empty list when the response omits availableCommands', async () => {
// A live ACP session entry that hasn't populated `availableCommands` yet
// is serialized without the field entirely (JSON drops `undefined`),
// even though the response type declares it non-optional. Regression
// test for #164: this must not leave `commands` as `undefined`.
apiMocks.getCached.mockResolvedValue({
source: 'thread',
commandsUpdatedAt: 0,
sessionMeta: {
availableModes: [],
currentModeId: null,
availableModels: [],
currentModelId: null,
configOptions: [],
selections: {},
sessionInfo: null,
usage: null,
updatedAt: 0,
},
});

await renderHarness();

expect(apiMocks.getCached).toHaveBeenCalledOnce();
expect(container?.textContent).toBe('');
});

it('refreshes the GET cache on later slash-menu intent', async () => {
apiMocks.getCached
.mockResolvedValueOnce({
Expand Down
7 changes: 6 additions & 1 deletion apps/web/src/hooks/useAcpSlashCommands.ts
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,12 @@ export function useAcpSlashCommands({
profileId,
);
if (!isCurrent()) return;
setCommands(response.availableCommands);
// `availableCommands` is dropped from the JSON body entirely when the
// server's live session entry hasn't populated it yet, so `response`
// can arrive without the field despite the response type saying
// otherwise. Falling back to `[]` keeps `commands` an array so every
// downstream `.length` read stays safe.
setCommands(response.availableCommands ?? []);
setError(null);
lastFetchedAtRef.current = Date.now();
} catch (value) {
Expand Down
Loading