@machina.ai/cell-cli 1.51.0-rc2 → 1.54.4-rc2
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 +3 -3
- package/dist/src/acp/acpSession.js +6 -1
- package/dist/src/acp/acpSession.js.map +1 -1
- package/dist/src/acp/acpSession.test.js +22 -0
- package/dist/src/acp/acpSession.test.js.map +1 -1
- package/dist/src/config/config.js +9 -3
- package/dist/src/config/config.js.map +1 -1
- package/dist/src/config/config.test.js +15 -2
- package/dist/src/config/config.test.js.map +1 -1
- package/dist/src/generated/git-commit.d.ts +2 -2
- package/dist/src/generated/git-commit.js +2 -2
- package/dist/src/nonInteractiveCli.js +27 -3
- package/dist/src/nonInteractiveCli.js.map +1 -1
- package/dist/src/nonInteractiveCli.test.js +48 -8
- package/dist/src/nonInteractiveCli.test.js.map +1 -1
- package/dist/src/nonInteractiveCliAgentSession.js +48 -5
- package/dist/src/nonInteractiveCliAgentSession.js.map +1 -1
- package/dist/src/nonInteractiveCliAgentSession.test.js +93 -1
- package/dist/src/nonInteractiveCliAgentSession.test.js.map +1 -1
- package/dist/src/ui/components/AsciiArt.d.ts +6 -6
- package/dist/src/ui/components/AsciiArt.js +6 -6
- package/dist/src/ui/components/ModelDialog.js +52 -11
- package/dist/src/ui/components/ModelDialog.js.map +1 -1
- package/dist/src/ui/components/ModelDialog.test.js +122 -66
- package/dist/src/ui/components/ModelDialog.test.js.map +1 -1
- package/dist/src/ui/contexts/SessionContext.js +12 -0
- package/dist/src/ui/contexts/SessionContext.js.map +1 -1
- package/dist/src/ui/hooks/useGeminiStream.js +81 -17
- package/dist/src/ui/hooks/useGeminiStream.js.map +1 -1
- package/dist/src/ui/hooks/useGeminiStream.test.js +218 -3
- package/dist/src/ui/hooks/useGeminiStream.test.js.map +1 -1
- package/dist/src/ui/hooks/usePrivacySettings.d.ts +7 -0
- package/dist/src/ui/hooks/usePrivacySettings.js +46 -3
- package/dist/src/ui/hooks/usePrivacySettings.js.map +1 -1
- package/dist/src/ui/hooks/usePrivacySettings.test.js +49 -4
- package/dist/src/ui/hooks/usePrivacySettings.test.js.map +1 -1
- package/dist/src/ui/hooks/useQuotaAndFallback.test.js +1 -1
- package/dist/src/ui/hooks/useQuotaAndFallback.test.js.map +1 -1
- package/dist/src/ui/privacy/CloudFreePrivacyNotice.js +6 -1
- package/dist/src/ui/privacy/CloudFreePrivacyNotice.js.map +1 -1
- package/dist/src/ui/privacy/CloudFreePrivacyNotice.test.js +10 -0
- package/dist/src/ui/privacy/CloudFreePrivacyNotice.test.js.map +1 -1
- package/dist/src/utils/handleAutoUpdate.test.js +2 -2
- package/dist/src/utils/sandbox-macos-permissive-open.sb +113 -4
- package/dist/src/utils/sandbox-macos-permissive-proxied.sb +111 -9
- package/dist/src/utils/sandbox-macos-profiles.test.d.ts +6 -0
- package/dist/src/utils/sandbox-macos-profiles.test.js +65 -0
- package/dist/src/utils/sandbox-macos-profiles.test.js.map +1 -0
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +3 -3
|
@@ -12,7 +12,7 @@ import { useGeminiStream } from './useGeminiStream.js';
|
|
|
12
12
|
import { useKeypress } from './useKeypress.js';
|
|
13
13
|
import * as atCommandProcessor from './atCommandProcessor.js';
|
|
14
14
|
import { useToolScheduler, } from './useToolScheduler.js';
|
|
15
|
-
import { CoreToolCallStatus, ApprovalMode, AuthType, GeminiEventType as ServerGeminiEventType, ToolErrorType, ToolConfirmationOutcome, MessageBusType, tokenLimit, debugLogger, coreEvents, CoreEvent, SHELL_TOOL_NAME, MCPDiscoveryState, GeminiCliOperation, getPlanModeExitMessage, UPDATE_TOPIC_TOOL_NAME, } from '@google/gemini-cli-core';
|
|
15
|
+
import { CoreToolCallStatus, ApprovalMode, DEFAULT_GEMINI_FLASH_MODEL, AuthType, GeminiEventType as ServerGeminiEventType, ToolErrorType, ToolConfirmationOutcome, MessageBusType, tokenLimit, debugLogger, coreEvents, CoreEvent, SHELL_TOOL_NAME, MCPDiscoveryState, GeminiCliOperation, getPlanModeExitMessage, UPDATE_TOPIC_TOOL_NAME, TRUE_EMPTY_RESPONSE_MESSAGE, } from '@google/gemini-cli-core';
|
|
16
16
|
import { MessageType, StreamingState } from '../types.js';
|
|
17
17
|
import { findLastSafeSplitPoint } from '../utils/markdownUtilities.js';
|
|
18
18
|
import { theme } from '../semantic-colors.js';
|
|
@@ -737,6 +737,77 @@ describe('useGeminiStream', () => {
|
|
|
737
737
|
expect(mockSendMessageStream).not.toHaveBeenCalled();
|
|
738
738
|
});
|
|
739
739
|
});
|
|
740
|
+
it('should record tool responses in history when the model was switched due to a quota error', async () => {
|
|
741
|
+
// Regression test: returning early on a quota-triggered model switch
|
|
742
|
+
// without recording the responses leaves the already-recorded
|
|
743
|
+
// functionCall unpaired, which corrupts all subsequent requests.
|
|
744
|
+
const responseParts = [
|
|
745
|
+
{
|
|
746
|
+
functionResponse: {
|
|
747
|
+
name: 'testTool',
|
|
748
|
+
id: 'call1',
|
|
749
|
+
response: { output: 'tool result' },
|
|
750
|
+
},
|
|
751
|
+
},
|
|
752
|
+
];
|
|
753
|
+
const completedToolCalls = [
|
|
754
|
+
{
|
|
755
|
+
request: {
|
|
756
|
+
callId: 'call1',
|
|
757
|
+
name: 'testTool',
|
|
758
|
+
args: {},
|
|
759
|
+
isClientInitiated: false,
|
|
760
|
+
prompt_id: 'prompt-id-quota',
|
|
761
|
+
},
|
|
762
|
+
status: CoreToolCallStatus.Success,
|
|
763
|
+
responseSubmittedToGemini: false,
|
|
764
|
+
response: {
|
|
765
|
+
callId: 'call1',
|
|
766
|
+
responseParts,
|
|
767
|
+
errorType: undefined,
|
|
768
|
+
},
|
|
769
|
+
tool: { displayName: 'MockTool' },
|
|
770
|
+
invocation: {
|
|
771
|
+
getDescription: () => `Mock description`,
|
|
772
|
+
},
|
|
773
|
+
},
|
|
774
|
+
];
|
|
775
|
+
const client = new MockedGeminiClientClass(mockConfig);
|
|
776
|
+
const mockConsumeUserHint = vi.fn(() => 'switch to the nprd database');
|
|
777
|
+
let capturedOnComplete = null;
|
|
778
|
+
mockUseToolScheduler.mockImplementation((onComplete) => {
|
|
779
|
+
capturedOnComplete = onComplete;
|
|
780
|
+
return [
|
|
781
|
+
[],
|
|
782
|
+
mockScheduleToolCalls,
|
|
783
|
+
mockMarkToolsAsSubmitted,
|
|
784
|
+
vi.fn(),
|
|
785
|
+
mockCancelAllToolCalls,
|
|
786
|
+
0,
|
|
787
|
+
];
|
|
788
|
+
});
|
|
789
|
+
await renderHookWithProviders(() => useGeminiStream(client, [], mockAddItem, mockConfig, mockLoadedSettings, mockOnDebugMessage, mockHandleSlashCommand, false, () => 'vscode', () => { }, () => Promise.resolve(), true, // modelSwitchedFromQuotaError
|
|
790
|
+
() => { }, () => { }, () => { }, 80, 24, false, mockConsumeUserHint));
|
|
791
|
+
await act(async () => {
|
|
792
|
+
if (capturedOnComplete) {
|
|
793
|
+
await new Promise((resolve) => setTimeout(resolve, 0));
|
|
794
|
+
await capturedOnComplete(completedToolCalls);
|
|
795
|
+
}
|
|
796
|
+
});
|
|
797
|
+
await waitFor(() => {
|
|
798
|
+
expect(mockMarkToolsAsSubmitted).toHaveBeenCalledWith(['call1']);
|
|
799
|
+
// The tool response must be paired with its functionCall in history,
|
|
800
|
+
// with no steering-hint text ahead of it...
|
|
801
|
+
expect(client.addHistory).toHaveBeenCalledWith({
|
|
802
|
+
role: 'user',
|
|
803
|
+
parts: responseParts,
|
|
804
|
+
});
|
|
805
|
+
// ...the turn must NOT auto-continue on the fallback model...
|
|
806
|
+
expect(mockSendMessageStream).not.toHaveBeenCalled();
|
|
807
|
+
// ...and the pending hint is left for the next real submit.
|
|
808
|
+
expect(mockConsumeUserHint).not.toHaveBeenCalled();
|
|
809
|
+
});
|
|
810
|
+
});
|
|
740
811
|
it('should NOT stop responding when only update_topic is called', async () => {
|
|
741
812
|
const topicToolCalls = [
|
|
742
813
|
{
|
|
@@ -1248,6 +1319,106 @@ describe('useGeminiStream', () => {
|
|
|
1248
1319
|
// The cancel function should be called
|
|
1249
1320
|
expect(mockCancelAllToolCalls).toHaveBeenCalled();
|
|
1250
1321
|
});
|
|
1322
|
+
it('should transition to Idle state when cancelled while a tool call is in progress and completes', async () => {
|
|
1323
|
+
const toolCalls = [
|
|
1324
|
+
{
|
|
1325
|
+
request: { callId: 'call1', name: 'tool1', args: {} },
|
|
1326
|
+
status: CoreToolCallStatus.Executing,
|
|
1327
|
+
responseSubmittedToGemini: false,
|
|
1328
|
+
tool: {
|
|
1329
|
+
name: 'tool1',
|
|
1330
|
+
description: 'desc1',
|
|
1331
|
+
build: vi.fn().mockImplementation((_) => ({
|
|
1332
|
+
getDescription: () => `Mock description`,
|
|
1333
|
+
})),
|
|
1334
|
+
},
|
|
1335
|
+
invocation: {
|
|
1336
|
+
getDescription: () => `Mock description`,
|
|
1337
|
+
},
|
|
1338
|
+
startTime: Date.now(),
|
|
1339
|
+
liveOutput: '...',
|
|
1340
|
+
},
|
|
1341
|
+
];
|
|
1342
|
+
const { result } = await renderTestHook(toolCalls);
|
|
1343
|
+
// State is `Responding` because a tool is running
|
|
1344
|
+
expect(result.current.streamingState).toBe(StreamingState.Responding);
|
|
1345
|
+
// Try to cancel
|
|
1346
|
+
simulateEscapeKeyPress();
|
|
1347
|
+
// Trigger the onComplete callback with the cancelled tool call
|
|
1348
|
+
await act(async () => {
|
|
1349
|
+
if (capturedOnComplete) {
|
|
1350
|
+
await capturedOnComplete([
|
|
1351
|
+
{
|
|
1352
|
+
...toolCalls[0],
|
|
1353
|
+
status: CoreToolCallStatus.Cancelled,
|
|
1354
|
+
response: {
|
|
1355
|
+
callId: 'call1',
|
|
1356
|
+
responseParts: [],
|
|
1357
|
+
},
|
|
1358
|
+
},
|
|
1359
|
+
]);
|
|
1360
|
+
}
|
|
1361
|
+
});
|
|
1362
|
+
// The final state should be idle because the cancelled tool call was marked as submitted
|
|
1363
|
+
expect(result.current.streamingState).toBe(StreamingState.Idle);
|
|
1364
|
+
});
|
|
1365
|
+
it('should append cancelled tool responses to history when cancelled while a tool call is in progress and completes with response parts', async () => {
|
|
1366
|
+
const toolCalls = [
|
|
1367
|
+
{
|
|
1368
|
+
request: { callId: 'call1', name: 'tool1', args: {} },
|
|
1369
|
+
status: CoreToolCallStatus.Executing,
|
|
1370
|
+
responseSubmittedToGemini: false,
|
|
1371
|
+
tool: {
|
|
1372
|
+
name: 'tool1',
|
|
1373
|
+
description: 'desc1',
|
|
1374
|
+
build: vi.fn().mockImplementation((_) => ({
|
|
1375
|
+
getDescription: () => `Mock description`,
|
|
1376
|
+
})),
|
|
1377
|
+
},
|
|
1378
|
+
invocation: {
|
|
1379
|
+
getDescription: () => `Mock description`,
|
|
1380
|
+
},
|
|
1381
|
+
startTime: Date.now(),
|
|
1382
|
+
liveOutput: '...',
|
|
1383
|
+
},
|
|
1384
|
+
];
|
|
1385
|
+
const { result, client } = await renderTestHook(toolCalls);
|
|
1386
|
+
// State is `Responding` because a tool is running
|
|
1387
|
+
expect(result.current.streamingState).toBe(StreamingState.Responding);
|
|
1388
|
+
// Try to cancel
|
|
1389
|
+
simulateEscapeKeyPress();
|
|
1390
|
+
const expectedResponseParts = [
|
|
1391
|
+
{
|
|
1392
|
+
functionResponse: {
|
|
1393
|
+
name: 'tool1',
|
|
1394
|
+
id: 'call1',
|
|
1395
|
+
response: { error: 'cancelled' },
|
|
1396
|
+
},
|
|
1397
|
+
},
|
|
1398
|
+
];
|
|
1399
|
+
// Trigger the onComplete callback with the cancelled tool call having non-empty response parts
|
|
1400
|
+
await act(async () => {
|
|
1401
|
+
if (capturedOnComplete) {
|
|
1402
|
+
await capturedOnComplete([
|
|
1403
|
+
{
|
|
1404
|
+
...toolCalls[0],
|
|
1405
|
+
status: CoreToolCallStatus.Cancelled,
|
|
1406
|
+
response: {
|
|
1407
|
+
callId: 'call1',
|
|
1408
|
+
responseParts: expectedResponseParts,
|
|
1409
|
+
},
|
|
1410
|
+
},
|
|
1411
|
+
]);
|
|
1412
|
+
}
|
|
1413
|
+
});
|
|
1414
|
+
// Assert that addHistory was called with the combined response parts
|
|
1415
|
+
expect(client.addHistory).toHaveBeenCalledWith({
|
|
1416
|
+
role: 'user',
|
|
1417
|
+
parts: expectedResponseParts,
|
|
1418
|
+
});
|
|
1419
|
+
// The final state should be idle because the cancelled tool call was marked as submitted
|
|
1420
|
+
expect(result.current.streamingState).toBe(StreamingState.Idle);
|
|
1421
|
+
});
|
|
1251
1422
|
it('should cancel a request when a tool is awaiting confirmation', async () => {
|
|
1252
1423
|
const mockOnConfirm = vi.fn().mockResolvedValue(undefined);
|
|
1253
1424
|
const toolCalls = [
|
|
@@ -1616,7 +1787,51 @@ describe('useGeminiStream', () => {
|
|
|
1616
1787
|
});
|
|
1617
1788
|
// 3. Assertion
|
|
1618
1789
|
await waitFor(() => {
|
|
1619
|
-
expect(mockParseAndFormatApiError).toHaveBeenCalledWith('Rate limit exceeded', mockAuthType, undefined, 'gemini-2.5-pro',
|
|
1790
|
+
expect(mockParseAndFormatApiError).toHaveBeenCalledWith('Rate limit exceeded', mockAuthType, undefined, 'gemini-2.5-pro', DEFAULT_GEMINI_FLASH_MODEL);
|
|
1791
|
+
});
|
|
1792
|
+
});
|
|
1793
|
+
it('should use TRUE_EMPTY_RESPONSE_MESSAGE when receiving an invalid stream event of type NO_RESPONSE_TEXT', async () => {
|
|
1794
|
+
mockSendMessageStream.mockClear();
|
|
1795
|
+
mockSendMessageStream.mockReturnValue((async function* () {
|
|
1796
|
+
yield {
|
|
1797
|
+
type: ServerGeminiEventType.InvalidStream,
|
|
1798
|
+
value: {
|
|
1799
|
+
type: 'NO_RESPONSE_TEXT',
|
|
1800
|
+
message: 'empty response text',
|
|
1801
|
+
},
|
|
1802
|
+
};
|
|
1803
|
+
})());
|
|
1804
|
+
const { result } = await renderTestHook();
|
|
1805
|
+
await act(async () => {
|
|
1806
|
+
await result.current.submitQuery('test query');
|
|
1807
|
+
});
|
|
1808
|
+
await waitFor(() => {
|
|
1809
|
+
expect(mockAddItem).toHaveBeenCalledWith(expect.objectContaining({
|
|
1810
|
+
type: MessageType.ERROR,
|
|
1811
|
+
text: TRUE_EMPTY_RESPONSE_MESSAGE,
|
|
1812
|
+
}), expect.any(Number));
|
|
1813
|
+
});
|
|
1814
|
+
});
|
|
1815
|
+
it('should use the event message when receiving a non-NO_RESPONSE_TEXT invalid stream event', async () => {
|
|
1816
|
+
mockSendMessageStream.mockClear();
|
|
1817
|
+
mockSendMessageStream.mockReturnValue((async function* () {
|
|
1818
|
+
yield {
|
|
1819
|
+
type: ServerGeminiEventType.InvalidStream,
|
|
1820
|
+
value: {
|
|
1821
|
+
type: 'MALFORMED_FUNCTION_CALL',
|
|
1822
|
+
message: 'Custom malformed function call message',
|
|
1823
|
+
},
|
|
1824
|
+
};
|
|
1825
|
+
})());
|
|
1826
|
+
const { result } = await renderTestHook();
|
|
1827
|
+
await act(async () => {
|
|
1828
|
+
await result.current.submitQuery('test query');
|
|
1829
|
+
});
|
|
1830
|
+
await waitFor(() => {
|
|
1831
|
+
expect(mockAddItem).toHaveBeenCalledWith(expect.objectContaining({
|
|
1832
|
+
type: MessageType.ERROR,
|
|
1833
|
+
text: 'Custom malformed function call message',
|
|
1834
|
+
}), expect.any(Number));
|
|
1620
1835
|
});
|
|
1621
1836
|
});
|
|
1622
1837
|
});
|
|
@@ -2369,7 +2584,7 @@ describe('useGeminiStream', () => {
|
|
|
2369
2584
|
}), expect.any(Number));
|
|
2370
2585
|
});
|
|
2371
2586
|
// Verify parseAndFormatApiError was called
|
|
2372
|
-
expect(mockParseAndFormatApiError).toHaveBeenCalledWith({ message: 'Test error' }, expect.any(String), undefined, 'gemini-2.5-pro',
|
|
2587
|
+
expect(mockParseAndFormatApiError).toHaveBeenCalledWith({ message: 'Test error' }, expect.any(String), undefined, 'gemini-2.5-pro', DEFAULT_GEMINI_FLASH_MODEL);
|
|
2373
2588
|
});
|
|
2374
2589
|
it('should update lastOutputTime on Gemini thought and content events', async () => {
|
|
2375
2590
|
vi.useFakeTimers();
|