Skip to content
Open
Show file tree
Hide file tree
Changes from 3 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
1 change: 1 addition & 0 deletions cli/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -247,6 +247,7 @@ notifications:
discord_webhook: "https://discord.com/api/webhooks/..."
slack_webhook: "https://hooks.slack.com/services/..."
custom_webhook: "https://your-api.com/webhook"
telemetry_webhook: "https://your-api.com/telemetry" # optional telemetry export hook
```

Notifications include task completion counts and status (completed/failed).
Expand Down
80 changes: 80 additions & 0 deletions cli/src/config/constants.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
export const PROGRESS_UPDATE_INTERVAL = 500;
export const HEARTBEAT_INTERVAL = 5000;
// Default retry count used by CLI/runtime options.
export const DEFAULT_MAX_RETRIES = 3;
// Legacy alias retained for older modules.
export const MAX_RETRIES = DEFAULT_MAX_RETRIES;
export const UI_LABELS = {
PLANNING: "[PLANNING]",
EXECUTION: "[EXECUTION]",
DONE: "[DONE]",
FAIL: "[FAIL]",
OK: "[OK]",
};
export const SPINNER_CHARS = ["|", "/", "-", "\\"];
export const MAX_EXECUTION_TIME = 300000; // 5 minutes
export const PROGRESS_POLL_INTERVAL = 2000;
export const WATCHER_DEBOUNCE = 250;
export const PLANNING_COOLDOWN = 2000;

// CLI Defaults
export const DEFAULT_RETRY_DELAY = 5;
export const DEFAULT_MAX_PARALLEL = 3;
export const DEFAULT_MAX_REPLANS = 3;
export const DEFAULT_MAX_ITERATIONS = 0;

// AI Engine Defaults
export const DEFAULT_AI_ENGINE_TIMEOUT_MS = 80 * 60 * 1000; // 80 minutes
export const STREAM_HEARTBEAT_INTERVAL_MS = 30000; // 30 seconds without output = potential hang

// Parallel Execution
export const CACHE_TTL_MS = 24 * 60 * 60 * 1000; // 24 hours
export const INITIAL_POOL_SIZE_MULTIPLIER = 1;
export const MAX_POOL_SIZE_MULTIPLIER = 5;
export const PLANNING_CONCURRENCY = 5;
export const POOL_INCREMENT = 2;

// Progress Monitoring
export const MAX_OPERATIONS_HISTORY = 10;
export const RECENT_ACTIONS_COUNT = 3;
export const FIND_WORKTREE_RETRIES = 20;
export const MAX_DISPLAYED_ACTIONS = 3;

// Sandbox Management
export const DEFAULT_MAX_SANDBOX_AGE_MS = 60 * 60 * 1000; // 1 hour
export const SANDBOX_STALE_THRESHOLD_MS = 24 * 60 * 60 * 1000; // 24 hours
export const SANDBOX_BACKGROUND_CLEANUP_DELAY_MS = 5 * 60 * 1000; // 5 minutes
export const MS_PER_MINUTE = 60000;
export const CLEANUP_DELAY_MS = 5000;
export const COPY_BACK_CONCURRENCY = 10;
export const SANDBOX_DIR_PREFIX = "agent-";
export const SANDBOX_SUFFIX = "";
export const DEFAULT_IGNORE_PATTERNS = [
".git",
"node_modules",
".ralphy-sandboxes",
".ralphy-worktrees",
".ralphy",
"agent-*",
"sandbox-*",
];
Comment on lines +52 to +60
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

DEFAULT_IGNORE_PATTERNS only matches root-level directories

The glob patterns here (e.g. node_modules, .git) compile to anchored regexes like ^node_modules$ and ^\.git$ (see globToRegex in file-indexer.ts lines 161–180). This means a top-level node_modules is correctly ignored. However, in monorepos with nested directories — e.g. packages/app/node_modules or packages/web/node_modules — the relative paths like packages/app/node_modules will not match ^node_modules$, causing the entire nested dependency tree to be indexed, resulting in severe performance degradation and bloated cache.

The patterns need a **/ prefix to match at any depth:

Suggested change
export const DEFAULT_IGNORE_PATTERNS = [
".git",
"node_modules",
".ralphy-sandboxes",
".ralphy-worktrees",
".ralphy",
"agent-*",
"sandbox-*",
];
export const DEFAULT_IGNORE_PATTERNS = [
"**/.git",
"**/node_modules",
"**/.ralphy-sandboxes",
"**/.ralphy-worktrees",
"**/.ralphy",
"agent-*",
"sandbox-*",
];
Prompt To Fix With AI
This is a comment left during a code review.
Path: cli/src/config/constants.ts
Line: 52-60

Comment:
**`DEFAULT_IGNORE_PATTERNS` only matches root-level directories**

The glob patterns here (e.g. `node_modules`, `.git`) compile to anchored regexes like `^node_modules$` and `^\.git$` (see `globToRegex` in `file-indexer.ts` lines 161–180). This means a top-level `node_modules` is correctly ignored. However, in monorepos with nested directories — e.g. `packages/app/node_modules` or `packages/web/node_modules` — the relative paths like `packages/app/node_modules` will **not** match `^node_modules$`, causing the entire nested dependency tree to be indexed, resulting in severe performance degradation and bloated cache.

The patterns need a `**/` prefix to match at any depth:

```suggestion
export const DEFAULT_IGNORE_PATTERNS = [
	"**/.git",
	"**/node_modules",
	"**/.ralphy-sandboxes",
	"**/.ralphy-worktrees",
	"**/.ralphy",
	"agent-*",
	"sandbox-*",
];
```

How can I resolve this? If you propose a fix, please make it concise.

Comment on lines +52 to +60
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

DEFAULT_IGNORE_PATTERNS missing common build-output directories

The list covers version-control and Ralphy-internal directories but omits the most common build artifact directories for TypeScript/JS, Rust, and Python ecosystems. In a mid-size monorepo, these can contain hundreds of thousands of files, making an unfiltered index extremely slow.

Add at minimum:

Suggested change
export const DEFAULT_IGNORE_PATTERNS = [
".git",
"node_modules",
".ralphy-sandboxes",
".ralphy-worktrees",
".ralphy",
"agent-*",
"sandbox-*",
];
export const DEFAULT_IGNORE_PATTERNS = [
".git",
"node_modules",
".ralphy-sandboxes",
".ralphy-worktrees",
".ralphy",
"agent-*",
"sandbox-*",
// Build artifacts
"dist",
"build",
"out",
".next",
".nuxt",
".svelte-kit",
"target", // Rust/Maven
"__pycache__",
".cache",
"coverage",
".turbo",
];

Note: these patterns are currently anchored to workspace root by globToRegex. Consider also updating globToRegex to support **/ prefixes for matching nested dependencies (e.g., packages/app/node_modules).

Prompt To Fix With AI
This is a comment left during a code review.
Path: cli/src/config/constants.ts
Line: 52-60

Comment:
`DEFAULT_IGNORE_PATTERNS` missing common build-output directories

The list covers version-control and Ralphy-internal directories but omits the most common build artifact directories for TypeScript/JS, Rust, and Python ecosystems. In a mid-size monorepo, these can contain hundreds of thousands of files, making an unfiltered index extremely slow.

Add at minimum:

```suggestion
export const DEFAULT_IGNORE_PATTERNS = [
	".git",
	"node_modules",
	".ralphy-sandboxes",
	".ralphy-worktrees",
	".ralphy",
	"agent-*",
	"sandbox-*",
	// Build artifacts
	"dist",
	"build",
	"out",
	".next",
	".nuxt",
	".svelte-kit",
	"target",       // Rust/Maven
	"__pycache__",
	".cache",
	"coverage",
	".turbo",
];
```

Note: these patterns are currently anchored to workspace root by `globToRegex`. Consider also updating `globToRegex` to support `**/` prefixes for matching nested dependencies (e.g., `packages/app/node_modules`).

How can I resolve this? If you propose a fix, please make it concise.


// File Utilities
export const MAX_FILE_SIZE_FOR_HASH = 2 * 1024 * 1024; // 2MB
export const DEFAULT_RECURSION_DEPTH = 5;

// Lock Management
export const LOCK_TIMEOUT_MS = 30000; // 30 seconds
export const LOCK_MAX_LOCKS = 5000; // Maximum number of locks
export const LOCK_DIR = ".ralphy/locks"; // Lock directory
export const LOCK_CLEANUP_INTERVAL_MS = 60000; // 1 minute between cleanups

// Path Constants
export const SANDBOX_DIR = ".ralphy-worktrees";
export const PLANNING_CACHE_FILE = "planning-cache.json";

// Hash Store Constants
export const HASH_STORE_DIR = ".ralphy-hashes";
export const HASH_STORE_MAX_AGE_MS = 24 * 60 * 60 * 1000; // 24 hours
export const HASH_REFERENCE_SUFFIX = ".hash-ref";
export const ENABLE_HASH_STORE = true; // Feature flag
4 changes: 4 additions & 0 deletions cli/src/config/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ export const NotificationsSchema = z.object({
discord_webhook: z.string().default(""),
slack_webhook: z.string().default(""),
custom_webhook: z.string().default(""),
telemetry_webhook: z.string().default(""),
});

/**
Expand Down Expand Up @@ -109,6 +110,8 @@ export interface RuntimeOptions {
browserEnabled: "auto" | "true" | "false";
/** Override default model for the engine */
modelOverride?: string;
/** Enable comprehensive OpenCode debugging */
debugOpenCode?: boolean;
/** Skip automatic branch merging after parallel execution */
skipMerge?: boolean;
/** Use lightweight sandboxes instead of git worktrees for parallel execution */
Expand Down Expand Up @@ -142,4 +145,5 @@ export const DEFAULT_OPTIONS: RuntimeOptions = {
githubLabel: "",
autoCommit: true,
browserEnabled: "auto",
debugOpenCode: false,
};
45 changes: 45 additions & 0 deletions cli/src/telemetry/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,51 @@ export interface ToolCall {
*/
export type TelemetryLevel = "anonymous" | "full";

/**
* Full session data for webhook
*/
interface WebhookSessionData {
sessionId: string;
engine: string;
Comment on lines +84 to +86
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Medium telemetry/types.ts:84

WebhookSessionData is missing the timestamp field from the Session interface, so the original session start time is dropped when constructing the webhook payload. The top-level timestamp in TelemetryWebhookPayload records the transmission time, not the session start time. Add timestamp: number to WebhookSessionData to preserve the session's original start time.

interface WebhookSessionData {
	sessionId: string;
+	timestamp: number;
	engine: string;
Also found in 1 other location(s)

cli/src/utils/json-validation.ts:31

The StepStartSchema does not define session ID fields (sessionID, sessionId, session_id) or enable passthrough. Zod z.object strips unknown keys by default. Consequently, if the upstream step_start event includes a session ID, it is stripped during parsing, causing the extractSessionId helper to return null and the session context to be lost.

🚀 Reply "fix it for me" or copy this AI Prompt for your agent:
In file cli/src/telemetry/types.ts around lines 84-86:

`WebhookSessionData` is missing the `timestamp` field from the `Session` interface, so the original session start time is dropped when constructing the webhook payload. The top-level `timestamp` in `TelemetryWebhookPayload` records the transmission time, not the session start time. Add `timestamp: number` to `WebhookSessionData` to preserve the session's original start time.

Evidence trail:
- cli/src/telemetry/types.ts lines 17-18: `Session` interface defines `timestamp: number; // Unix ms`
- cli/src/telemetry/types.ts lines 84-100: `WebhookSessionData` interface - no `timestamp` field present
- cli/src/telemetry/webhook.ts line 28: `timestamp: new Date().toISOString()` sets transmission time
- cli/src/telemetry/webhook.ts lines 29-42: `session` object construction copies fields from Session but omits `session.timestamp`

Also found in 1 other location(s):
- cli/src/utils/json-validation.ts:31 -- The `StepStartSchema` does not define session ID fields (`sessionID`, `sessionId`, `session_id`) or enable passthrough. Zod `z.object` strips unknown keys by default. Consequently, if the upstream `step_start` event includes a session ID, it is stripped during parsing, causing the `extractSessionId` helper to return `null` and the session context to be lost.

mode: string;
cliVersion: string;
platform: string;
totalTokensIn: number;
totalTokensOut: number;
totalDurationMs: number;
taskCount: number;
successCount: number;
failedCount: number;
toolCalls: {
toolName: string;
callCount: number;
successCount: number;
failedCount: number;
avgDurationMs: number;
}[];
tags?: string[];
}

/**
* Full session details for webhook (full privacy mode)
*/
interface WebhookSessionDetails {
prompt?: string;
response?: string;
filePaths?: string[];
}
Comment on lines +84 to +113
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

WebhookSessionData and WebhookSessionDetails not exported

These two interfaces form the public contract of TelemetryWebhookPayload (as field types session and details) but are not exported. Consumers that need to work with these fields (e.g., to type-check a webhook handler) cannot declare typed local variables without repeating the shape inline.

Export them explicitly:

Suggested change
interface WebhookSessionData {
sessionId: string;
engine: string;
mode: string;
cliVersion: string;
platform: string;
totalTokensIn: number;
totalTokensOut: number;
totalDurationMs: number;
taskCount: number;
successCount: number;
failedCount: number;
toolCalls: {
toolName: string;
callCount: number;
successCount: number;
failedCount: number;
avgDurationMs: number;
}[];
tags?: string[];
}
/**
* Full session details for webhook (full privacy mode)
*/
interface WebhookSessionDetails {
prompt?: string;
response?: string;
filePaths?: string[];
}
export interface WebhookSessionData {
sessionId: string;
engine: string;
mode: string;
cliVersion: string;
platform: string;
totalTokensIn: number;
totalTokensOut: number;
totalDurationMs: number;
taskCount: number;
successCount: number;
failedCount: number;
toolCalls: {
toolName: string;
callCount: number;
successCount: number;
failedCount: number;
avgDurationMs: number;
}[];
tags?: string[];
}
/**
* Full session details for webhook (full privacy mode)
*/
export interface WebhookSessionDetails {
prompt?: string;
response?: string;
filePaths?: string[];
}
Prompt To Fix With AI
This is a comment left during a code review.
Path: cli/src/telemetry/types.ts
Line: 84-113

Comment:
`WebhookSessionData` and `WebhookSessionDetails` not exported

These two interfaces form the public contract of `TelemetryWebhookPayload` (as field types `session` and `details`) but are not exported. Consumers that need to work with these fields (e.g., to type-check a webhook handler) cannot declare typed local variables without repeating the shape inline.

Export them explicitly:

```suggestion
export interface WebhookSessionData {
	sessionId: string;
	engine: string;
	mode: string;
	cliVersion: string;
	platform: string;
	totalTokensIn: number;
	totalTokensOut: number;
	totalDurationMs: number;
	taskCount: number;
	successCount: number;
	failedCount: number;
	toolCalls: {
		toolName: string;
		callCount: number;
		successCount: number;
		failedCount: number;
		avgDurationMs: number;
	}[];
	tags?: string[];
}

/**
 * Full session details for webhook (full privacy mode)
 */
export interface WebhookSessionDetails {
	prompt?: string;
	response?: string;
	filePaths?: string[];
}
```

How can I resolve this? If you propose a fix, please make it concise.


/**
* Telemetry webhook payload
*/
export interface TelemetryWebhookPayload {
event: string;
version: string;
timestamp: string;
session: WebhookSessionData;
details?: WebhookSessionDetails;
}

/**
* Telemetry configuration
*/
Expand Down
Loading