Files
gemini-cli/packages/cli/src/ui/components/ScheduledWorkDisplay.tsx
T
Sandy Tao 1a6f730439 feat: add Forever Mode with time-based scheduled work and A2A listener
Add --forever CLI flag that enables autonomous agent operation with
scheduled work, context management, A2A protocol support, and session
optimization.

Core features:
- Time-based WorkScheduler: manages a sorted list of scheduled prompts
  that fire at absolute or relative times, persisted across sessions
- schedule_work tool: add/cancel scheduled prompts with 'at' (local
  time) or 'inMinutes' (relative) params; current time and schedule
  prepended to every model turn
- A2A HTTP listener: JSON-RPC 2.0 server bridges external messages
  into the session (message/send, tasks/get, responses/poll)
- PreCompress hook: hooks can return newHistory to replace built-in
  LLM compression
- Idle hook: fires after configurable inactivity, can auto-submit
  prompts
- Forever mode disables MemoryTool, EnterPlanModeTool, interactive
  shell

UI:
- ScheduledWorkDisplay component shows all pending items above the
  context summary bar
- A2A port shown in StatusDisplay when active

Session optimization for long-running sessions:
- Record lastCompressionIndex on ConversationRecord; on resume, only
  load post-compression messages
- Restore scheduled work items on session resume (past-due fire
  immediately)
- Skip file I/O in updateMessagesFromHistory when no tool results to
  sync
- Prune UI history to last 50 items after each context compression
2026-03-18 11:32:57 -07:00

67 lines
1.7 KiB
TypeScript

/**
* @license
* Copyright 2026 Google LLC
* SPDX-License-Identifier: Apache-2.0
*/
import type React from 'react';
import { useState, useEffect } from 'react';
import { Box, Text } from 'ink';
import { theme } from '../semantic-colors.js';
import { useConfig } from '../contexts/ConfigContext.js';
import type { ScheduledItem } from '@google/gemini-cli-core';
/**
* Displays all pending scheduled work items above the context summary.
* Only renders when there are pending items.
*/
export const ScheduledWorkDisplay: React.FC = () => {
const config = useConfig();
const [items, setItems] = useState<readonly ScheduledItem[]>([]);
useEffect(() => {
const scheduler = config.getWorkScheduler();
const update = () => {
setItems(scheduler.getPendingItems());
};
scheduler.on('changed', update);
update();
return () => {
scheduler.off('changed', update);
};
}, [config]);
if (items.length === 0) {
return null;
}
return (
<Box flexDirection="column" paddingX={1}>
<Text color={theme.text.secondary}>
Scheduled work ({items.length}):
</Text>
{items.map((item) => {
const timeStr = item.fireAt.toLocaleTimeString([], {
hour: '2-digit',
minute: '2-digit',
});
const diffMs = item.fireAt.getTime() - Date.now();
const diffMins = Math.max(0, Math.ceil(diffMs / 60000));
const truncatedPrompt =
item.prompt.length > 60
? item.prompt.slice(0, 57) + '...'
: item.prompt;
return (
<Text key={item.id} color={theme.text.secondary}>
{' '}
{timeStr} (in {diffMins}m) {truncatedPrompt}
</Text>
);
})}
</Box>
);
};