Skip to content
Open
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
29 changes: 29 additions & 0 deletions source/cydo/domain/tasks/model.d
Original file line number Diff line number Diff line change
Expand Up @@ -975,6 +975,25 @@ struct TaskHistoryStartMessage
string type = "task_history_start";
int tid;
int total;
int window_start; // first replayed seq; > 0 means older history exists unsent
int window_limit; // window applied, in messages; 0 = the whole history
}

/// Frames a replay of the messages *older* than what the client already holds,
/// so it can prepend them instead of rebuilding its list.
struct TaskHistoryPrependStartMessage
{
string type = "task_history_prepend_start";
int tid;
int window_start; // first seq in this batch; 0 means the task's beginning
int before_seq; // where the batch stops, i.e. the client's current start
}

struct TaskHistoryPrependEndMessage
{
string type = "task_history_prepend_end";
int tid;
int window_start;
}

struct TaskHistoryEndMessage
Expand Down Expand Up @@ -1031,6 +1050,14 @@ struct WsMessage
@JSONOptional Nullable!uint expected_num_turns;
string correlation_id;
string tool_use_id;
// request_history: 0 lets the server pick from its config for device_class,
// >0 is an explicit window, -1 asks for the whole history. the client is not
// asked to know the configured window, because it learns that from
// server_status, which arrives after the tasks list that triggers the first
// history request
@JSONOptional int limit;
@JSONOptional string device_class; // "mobile" or "desktop"
@JSONOptional int before_seq; // request_history_before: older than this
}

struct TaskCreatedMessage
Expand Down Expand Up @@ -1168,6 +1195,8 @@ struct ServerStatusMessage
bool auth_enabled;
bool dev_mode;
string build_id;
int history_window_desktop; // initial replay window in messages, 0 = full
int history_window_mobile;
}

struct ScanStatusMessage
Expand Down
10 changes: 10 additions & 0 deletions source/cydo/runtime/config/package.d
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,12 @@ struct WorkspaceConfig
@Optional ProjectDiscoveryConfig project_discovery;
}

struct HistoryWindowConfig
{
@Optional int desktop;
@Optional int mobile;
}

struct CydoConfig
{
@Key("name") WorkspaceConfig[] workspaces;
Expand All @@ -115,6 +121,10 @@ struct CydoConfig
@Optional bool dev_mode;
@Optional string log_level = "info";
@Optional string system_keyword = "SYSTEM";
/// How much of a task's history to replay when it is opened, in messages
/// (user and assistant bubbles), per device class. Absent or zero replays
/// everything, which is the default.
@Optional HistoryWindowConfig history_window;

/// Called by configy during parsing (configy/read.d:650), so a semantic
/// error surfaces on the same path as a YAML syntax error.
Expand Down
30 changes: 29 additions & 1 deletion source/cydo/server/app.d
Original file line number Diff line number Diff line change
Expand Up @@ -1099,6 +1099,8 @@ class App
authUser.length > 0 || authPass.length > 0,
config.dev_mode,
webDistDir,
config.history_window.desktop,
config.history_window.mobile,
).representation));
ws.send(Data(buildNoticesList(activeNotices).representation));
if (discoveryService.scanInProgress)
Expand Down Expand Up @@ -1265,6 +1267,7 @@ class App
{
case "create_task": handleCreateTaskMsg(ws, json); break;
case "request_history": handleRequestHistory(ws, json); break;
case "request_history_before": handleRequestHistoryBefore(ws, json); break;
case "message": handleUserMessage(json); break;
case "resume": handleResumeMsg(json); break;
case "interrupt": handleInterruptMsg(json); break;
Expand Down Expand Up @@ -1460,7 +1463,30 @@ class App

private void handleRequestHistory(WebSocketAdapter ws, WsMessage json)
{
historyPipeline.handleRequestHistory(ws, json.tid);
historyPipeline.handleRequestHistory(ws, json.tid,
resolveHistoryLimit(json.limit, json.device_class));
}

private void handleRequestHistoryBefore(WebSocketAdapter ws, WsMessage json)
{
historyPipeline.handleRequestHistoryBefore(ws, json.tid, json.before_seq,
resolveHistoryLimit(json.limit, json.device_class));
}

/// Turn a client's request into an actual window size.
///
/// The server owns the numbers because it is the only side that always
/// knows them: a client that has not yet processed server_status would
/// otherwise ask for everything, which on a long task means replaying tens
/// of thousands of events.
private int resolveHistoryLimit(int requested, string deviceClass)
{
if (requested != 0)
return requested < 0 ? 0 : requested; // negative asks for it all
auto window = deviceClass == "mobile"
? config.history_window.mobile
: config.history_window.desktop;
return window > 0 ? window : 0;
}

private void sendHistoryReplaySupplementalState(WebSocketAdapter ws, int tid)
Expand Down Expand Up @@ -3395,6 +3421,8 @@ class App
authUser.length > 0 || authPass.length > 0,
config.dev_mode,
webDistDir,
config.history_window.desktop,
config.history_window.mobile,
));
infof("Config reloaded successfully");
discoveryService.endScan();
Expand Down
5 changes: 4 additions & 1 deletion source/cydo/web/snapshots.d
Original file line number Diff line number Diff line change
Expand Up @@ -90,13 +90,16 @@ string readBuildId(string webDistDir)
return m[1].idup;
}

string buildServerStatus(bool authEnabled, bool devMode, string webDistDir)
string buildServerStatus(bool authEnabled, bool devMode, string webDistDir,
int historyWindowDesktop = 0, int historyWindowMobile = 0)
{
return toJson(ServerStatusMessage(
"server_status",
authEnabled,
devMode,
readBuildId(webDistDir),
historyWindowDesktop,
historyWindowMobile,
));
}

Expand Down
Loading