Add comprehensive command and skill reference documentation in Chinese

- Created a new document for command and skill references, detailing orchestrator commands, workflow session commands, issue workflow commands, IDAW commands, with-file workflows, cycle workflows, CLI commands, memory commands, team skills, workflow skills, utility skills, and Codex capabilities.
- Added a comparison table for workflows, outlining their best uses, levels, self-containment, and automatic chaining behavior.
This commit is contained in:
catlog22
2026-03-02 17:41:40 +08:00
parent 3bb4a821de
commit b780734649
12 changed files with 2215 additions and 205 deletions

View File

@@ -1093,7 +1093,7 @@ export async function handleCliRoutes(ctx: RouteContext): Promise<boolean> {
return { error: 'Execution not found', status: 404 };
}
const review = historyStore.saveReview({
const review = await historyStore.saveReview({
execution_id: executionId,
status,
rating,

View File

@@ -589,7 +589,7 @@ Return ONLY valid JSON in this exact format (no markdown, no code blocks, just p
const storeModule = await import('../../tools/cli-history-store.js');
const store = storeModule.getHistoryStore(projectPath);
const insightId = `insight-${Date.now()}`;
store.saveInsight({
await store.saveInsight({
id: insightId,
tool,
promptCount: prompts.length,

View File

@@ -24,6 +24,212 @@ export const wsClients = new Set<Duplex>();
// Track message counts per client for rate limiting
export const wsClientMessageCounts = new Map<Duplex, { count: number; resetTime: number }>();
/**
* Universal broadcast throttling system
* Reduces WebSocket traffic by deduplicating and rate-limiting broadcast messages
*/
interface ThrottleEntry {
lastSend: number;
pendingData: unknown;
}
type ThrottleCategory = 'state_update' | 'memory_cpu' | 'log_output' | 'immediate';
/** Map of message type to throttle configuration */
const THROTTLE_CONFIG = new Map<string, { interval: number; category: ThrottleCategory }>(
[
// State updates - high frequency, low value when duplicated
['LOOP_STATE_UPDATE', { interval: 1000, category: 'state_update' }],
['ORCHESTRATOR_STATE_UPDATE', { interval: 1000, category: 'state_update' }],
['COORDINATOR_STATE_UPDATE', { interval: 1000, category: 'state_update' }],
['QUEUE_SCHEDULER_STATE_UPDATE', { interval: 1000, category: 'state_update' }],
// Memory/CPU updates - medium frequency
['LOOP_STEP_COMPLETED', { interval: 500, category: 'memory_cpu' }],
['ORCHESTRATOR_NODE_COMPLETED', { interval: 500, category: 'memory_cpu' }],
['COORDINATOR_COMMAND_COMPLETED', { interval: 500, category: 'memory_cpu' }],
['QUEUE_ITEM_UPDATED', { interval: 500, category: 'memory_cpu' }],
// Log/output - higher frequency allowed for real-time streaming
['LOOP_LOG_ENTRY', { interval: 200, category: 'log_output' }],
['ORCHESTRATOR_LOG', { interval: 200, category: 'log_output' }],
['COORDINATOR_LOG_ENTRY', { interval: 200, category: 'log_output' }],
// Item added/removed - send immediately
['QUEUE_ITEM_ADDED', { interval: 0, category: 'immediate' }],
['QUEUE_ITEM_REMOVED', { interval: 0, category: 'immediate' }],
['QUEUE_SCHEDULER_CONFIG_UPDATED', { interval: 0, category: 'immediate' }],
['ORCHESTRATOR_NODE_STARTED', { interval: 0, category: 'immediate' }],
['ORCHESTRATOR_NODE_FAILED', { interval: 0, category: 'immediate' }],
['COORDINATOR_COMMAND_STARTED', { interval: 0, category: 'immediate' }],
['COORDINATOR_COMMAND_FAILED', { interval: 0, category: 'immediate' }],
['COORDINATOR_QUESTION_ASKED', { interval: 0, category: 'immediate' }],
['COORDINATOR_ANSWER_RECEIVED', { interval: 0, category: 'immediate' }],
['LOOP_COMPLETED', { interval: 0, category: 'immediate' }],
['LOOP_COMPLETED' as any, { interval: 0, category: 'immediate' }],
].filter(([key]) => key !== 'LOOP_COMPLETED' as any)
);
// Add LOOP_COMPLETED separately to avoid type issues
THROTTLE_CONFIG.set('LOOP_COMPLETED', { interval: 0, category: 'immediate' });
/** Per-message-type throttle tracking */
const throttleState = new Map<string, ThrottleEntry>();
/** Metrics for broadcast optimization */
export const broadcastMetrics = {
sent: 0,
throttled: 0,
deduped: 0,
};
/**
* Get throttle configuration for a message type
*/
function getThrottleConfig(messageType: string): { interval: number; category: ThrottleCategory } {
return THROTTLE_CONFIG.get(messageType) || { interval: 0, category: 'immediate' };
}
/**
* Serialize message data for comparison
*/
function serializeMessage(data: unknown): string {
if (typeof data === 'string') return data;
if (typeof data === 'object' && data !== null) {
return JSON.stringify(data, Object.keys(data).sort());
}
return String(data);
}
/**
* Create WebSocket frame
*/
export function createWebSocketFrame(data: unknown): Buffer {
const payload = Buffer.from(JSON.stringify(data), 'utf8');
const length = payload.length;
let frame;
if (length <= 125) {
frame = Buffer.alloc(2 + length);
frame[0] = 0x81; // Text frame, FIN
frame[1] = length;
payload.copy(frame, 2);
} else if (length <= 65535) {
frame = Buffer.alloc(4 + length);
frame[0] = 0x81;
frame[1] = 126;
frame.writeUInt16BE(length, 2);
payload.copy(frame, 4);
} else {
frame = Buffer.alloc(10 + length);
frame[0] = 0x81;
frame[1] = 127;
frame.writeBigUInt64BE(BigInt(length), 2);
payload.copy(frame, 10);
}
return frame;
}
/**
* Broadcast message to all connected WebSocket clients with universal throttling
* - Deduplicates identical messages within throttle window
* - Rate-limits by message type with adaptive intervals
* - Preserves message ordering within each type
*/
export function broadcastToClients(data: unknown): void {
const eventType =
typeof data === 'object' && data !== null && 'type' in data ? (data as { type?: unknown }).type : undefined;
if (!eventType || typeof eventType !== 'string') {
// Unknown message type - send immediately
const frame = createWebSocketFrame(data);
for (const client of wsClients) {
try {
client.write(frame);
} catch (e) {
wsClients.delete(client);
}
}
console.log(`[WS] Broadcast to ${wsClients.size} clients: unknown type`);
return;
}
const config = getThrottleConfig(eventType);
const now = Date.now();
const state = throttleState.get(eventType);
if (config.interval === 0) {
// Immediate - send without throttling
const frame = createWebSocketFrame(data);
for (const client of wsClients) {
try {
client.write(frame);
} catch (e) {
wsClients.delete(client);
}
}
broadcastMetrics.sent++;
throttleState.set(eventType, { lastSend: now, pendingData: data });
console.log(`[WS] Broadcast to ${wsClients.size} clients: ${eventType} (immediate)`);
return;
}
// Check if we should throttle
const currentDataHash = serializeMessage(data);
if (state) {
const timeSinceLastSend = now - state.lastSend;
// Check for duplicate data
if (timeSinceLastSend < config.interval) {
const pendingDataHash = serializeMessage(state.pendingData);
if (currentDataHash === pendingDataHash) {
// Duplicate message - drop it
broadcastMetrics.deduped++;
console.log(`[WS] Throttled duplicate ${eventType} (${timeSinceLastSend}ms since last)`);
return;
}
// Different data but within throttle window - update pending
throttleState.set(eventType, { lastSend: state.lastSend, pendingData: data });
broadcastMetrics.throttled++;
console.log(`[WS] Throttled ${eventType} (${timeSinceLastSend}ms since last, pending updated)`);
return;
}
}
// Send the message
const frame = createWebSocketFrame(data);
for (const client of wsClients) {
try {
client.write(frame);
} catch (e) {
wsClients.delete(client);
}
}
broadcastMetrics.sent++;
throttleState.set(eventType, { lastSend: now, pendingData: data });
console.log(`[WS] Broadcast to ${wsClients.size} clients: ${eventType}`);
}
/**
* Get broadcast throttling metrics
*/
export function getBroadcastMetrics(): Readonly<typeof broadcastMetrics> {
return { ...broadcastMetrics };
}
/**
* Reset broadcast throttling metrics (for testing/monitoring)
*/
export function resetBroadcastMetrics(): void {
broadcastMetrics.sent = 0;
broadcastMetrics.throttled = 0;
broadcastMetrics.deduped = 0;
}
/**
* Check if a new WebSocket connection should be accepted
* Returns true if connection allowed, false if limit reached
@@ -387,25 +593,6 @@ export function createWebSocketFrame(data: unknown): Buffer {
return frame;
}
/**
* Broadcast message to all connected WebSocket clients
*/
export function broadcastToClients(data: unknown): void {
const frame = createWebSocketFrame(data);
for (const client of wsClients) {
try {
client.write(frame);
} catch (e) {
wsClients.delete(client);
}
}
const eventType =
typeof data === 'object' && data !== null && 'type' in data ? (data as { type?: unknown }).type : undefined;
console.log(`[WS] Broadcast to ${wsClients.size} clients:`, eventType);
}
/**
* Extract session ID from file path
*/
@@ -435,12 +622,9 @@ export function extractSessionIdFromPath(filePath: string): string | null {
}
/**
* Loop-specific broadcast with throttling
* Throttles LOOP_STATE_UPDATE messages to avoid flooding clients
* Loop broadcast types (without timestamp - added automatically)
* Throttling is handled universally in broadcastToClients
*/
let lastLoopBroadcast = 0;
const LOOP_BROADCAST_THROTTLE = 1000; // 1 second
export type LoopMessage =
| Omit<LoopStateUpdateMessage, 'timestamp'>
| Omit<LoopStepCompletedMessage, 'timestamp'>
@@ -448,18 +632,10 @@ export type LoopMessage =
| Omit<LoopLogEntryMessage, 'timestamp'>;
/**
* Broadcast loop state update with throttling
* Broadcast loop update with automatic throttling
* Note: Throttling is now handled universally in broadcastToClients
*/
export function broadcastLoopUpdate(message: LoopMessage): void {
const now = Date.now();
// Throttle LOOP_STATE_UPDATE to reduce WebSocket traffic
if (message.type === 'LOOP_STATE_UPDATE' && now - lastLoopBroadcast < LOOP_BROADCAST_THROTTLE) {
return;
}
lastLoopBroadcast = now;
broadcastToClients({
...message,
timestamp: new Date().toISOString()
@@ -467,8 +643,8 @@ export function broadcastLoopUpdate(message: LoopMessage): void {
}
/**
* Broadcast loop log entry (no throttling)
* Used for streaming real-time logs to Dashboard
* Broadcast loop log entry
* Note: Throttling is now handled universally in broadcastToClients
*/
export function broadcastLoopLog(loop_id: string, step_id: string, line: string): void {
broadcastToClients({
@@ -482,6 +658,7 @@ export function broadcastLoopLog(loop_id: string, step_id: string, line: string)
/**
* Union type for Orchestrator messages (without timestamp - added automatically)
* Throttling is handled universally in broadcastToClients
*/
export type OrchestratorMessage =
| Omit<OrchestratorStateUpdateMessage, 'timestamp'>
@@ -491,29 +668,10 @@ export type OrchestratorMessage =
| Omit<OrchestratorLogMessage, 'timestamp'>;
/**
* Orchestrator-specific broadcast with throttling
* Throttles ORCHESTRATOR_STATE_UPDATE messages to avoid flooding clients
*/
let lastOrchestratorBroadcast = 0;
const ORCHESTRATOR_BROADCAST_THROTTLE = 1000; // 1 second
/**
* Broadcast orchestrator update with throttling
* STATE_UPDATE messages are throttled to 1 per second
* Other message types are sent immediately
* Broadcast orchestrator update with automatic throttling
* Note: Throttling is now handled universally in broadcastToClients
*/
export function broadcastOrchestratorUpdate(message: OrchestratorMessage): void {
const now = Date.now();
// Throttle ORCHESTRATOR_STATE_UPDATE to reduce WebSocket traffic
if (message.type === 'ORCHESTRATOR_STATE_UPDATE' && now - lastOrchestratorBroadcast < ORCHESTRATOR_BROADCAST_THROTTLE) {
return;
}
if (message.type === 'ORCHESTRATOR_STATE_UPDATE') {
lastOrchestratorBroadcast = now;
}
broadcastToClients({
...message,
timestamp: new Date().toISOString()
@@ -521,8 +679,8 @@ export function broadcastOrchestratorUpdate(message: OrchestratorMessage): void
}
/**
* Broadcast orchestrator log entry (no throttling)
* Used for streaming real-time execution logs to Dashboard
* Broadcast orchestrator log entry
* Note: Throttling is now handled universally in broadcastToClients
*/
export function broadcastOrchestratorLog(execId: string, log: Omit<ExecutionLog, 'timestamp'>): void {
broadcastToClients({
@@ -639,6 +797,7 @@ export interface CoordinatorAnswerReceivedMessage {
/**
* Union type for Coordinator messages (without timestamp - added automatically)
* Throttling is handled universally in broadcastToClients
*/
export type CoordinatorMessage =
| Omit<CoordinatorStateUpdateMessage, 'timestamp'>
@@ -650,29 +809,10 @@ export type CoordinatorMessage =
| Omit<CoordinatorAnswerReceivedMessage, 'timestamp'>;
/**
* Coordinator-specific broadcast with throttling
* Throttles COORDINATOR_STATE_UPDATE messages to avoid flooding clients
*/
let lastCoordinatorBroadcast = 0;
const COORDINATOR_BROADCAST_THROTTLE = 1000; // 1 second
/**
* Broadcast coordinator update with throttling
* STATE_UPDATE messages are throttled to 1 per second
* Other message types are sent immediately
* Broadcast coordinator update with automatic throttling
* Note: Throttling is now handled universally in broadcastToClients
*/
export function broadcastCoordinatorUpdate(message: CoordinatorMessage): void {
const now = Date.now();
// Throttle COORDINATOR_STATE_UPDATE to reduce WebSocket traffic
if (message.type === 'COORDINATOR_STATE_UPDATE' && now - lastCoordinatorBroadcast < COORDINATOR_BROADCAST_THROTTLE) {
return;
}
if (message.type === 'COORDINATOR_STATE_UPDATE') {
lastCoordinatorBroadcast = now;
}
broadcastToClients({
...message,
timestamp: new Date().toISOString()
@@ -680,8 +820,8 @@ export function broadcastCoordinatorUpdate(message: CoordinatorMessage): void {
}
/**
* Broadcast coordinator log entry (no throttling)
* Used for streaming real-time coordinator logs to Dashboard
* Broadcast coordinator log entry
* Note: Throttling is now handled universally in broadcastToClients
*/
export function broadcastCoordinatorLog(
executionId: string,
@@ -715,6 +855,7 @@ export type {
/**
* Union type for Queue messages (without timestamp - added automatically)
* Throttling is handled universally in broadcastToClients
*/
export type QueueMessage =
| Omit<QueueSchedulerStateUpdateMessage, 'timestamp'>
@@ -724,29 +865,10 @@ export type QueueMessage =
| Omit<QueueSchedulerConfigUpdatedMessage, 'timestamp'>;
/**
* Queue-specific broadcast with throttling
* Throttles QUEUE_SCHEDULER_STATE_UPDATE messages to avoid flooding clients
*/
let lastQueueBroadcast = 0;
const QUEUE_BROADCAST_THROTTLE = 1000; // 1 second
/**
* Broadcast queue update with throttling
* STATE_UPDATE messages are throttled to 1 per second
* Other message types are sent immediately
* Broadcast queue update with automatic throttling
* Note: Throttling is now handled universally in broadcastToClients
*/
export function broadcastQueueUpdate(message: QueueMessage): void {
const now = Date.now();
// Throttle QUEUE_SCHEDULER_STATE_UPDATE to reduce WebSocket traffic
if (message.type === 'QUEUE_SCHEDULER_STATE_UPDATE' && now - lastQueueBroadcast < QUEUE_BROADCAST_THROTTLE) {
return;
}
if (message.type === 'QUEUE_SCHEDULER_STATE_UPDATE') {
lastQueueBroadcast = now;
}
broadcastToClients({
...message,
timestamp: new Date().toISOString()