Skip to content

Commit 7d24e55

Browse files
committed
feat: full parity with .NET SDK v0.1.23+
Implement all parity gaps (Gaps 1-14) for complete feature parity with the official Copilot SDK .NET reference (v0.1.23+). Hooks System: - 6 hook types: PreToolUse, PostToolUse, UserPromptSubmitted, SessionStart, SessionEnd, ErrorOccurred - SessionHooks config with handler registration - hooks.invoke RPC handler wired into session create/resume User Input Handler: - UserInputRequest/Response/Invocation types - userInput.request RPC handler for ask_user tool support Reasoning Effort: - reasoning_effort on SessionConfig and ResumeSessionConfig - Model capability fields (supported_reasoning_efforts, default_reasoning_effort) New Event Types: - session.shutdown (ShutdownType, CodeChanges, metrics) - session.snapshot_rewind (upToEventId, eventsRemoved) - skill.invoked (name, path, content, allowedTools) Extended Event Fields: - SessionError: statusCode, providerCallId - CompactionComplete: checkpointNumber, checkpointPath - AssistantMessage: reasoningOpaque, reasoningText, encryptedContent - AssistantUsage: parentToolCallId - ToolExecutionStart: mcpServerName - ToolExecutionComplete: detailedContent Additional Features: - Client auth options (github_token, use_logged_in_user) with validation - list_models caching with mutex + cache clear on disconnect - ResumeSessionConfig full parity with SessionConfig - Selection attachment type with start/end positions - Session lifecycle event types - Subagent wire format fix (subagent.* + legacy custom_agent.*) - 4 missing event types (compaction start/complete, usage info, tool progress) - ModelVisionLimits + GetModelsResponse wrapper Examples (3 new): - hooks.cpp: all 6 hook types with interactive chat - user_input.cpp: choice-based and freeform input handling - reasoning_effort.cpp: model capability query + effort config Tests: 215 unit + 60 E2E (275 total, 0 failures, 16 BYOK-skipped)
1 parent a3565bd commit 7d24e55

File tree

15 files changed

+4524
-10
lines changed

15 files changed

+4524
-10
lines changed

examples/CMakeLists.txt

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,3 +54,33 @@ set_target_properties(attachments PROPERTIES FOLDER "Examples")
5454
add_executable(fluent_tools fluent_tools.cpp)
5555
target_link_libraries(fluent_tools PRIVATE copilot_sdk_cpp)
5656
set_target_properties(fluent_tools PROPERTIES FOLDER "Examples")
57+
58+
# Compaction events monitoring example
59+
add_executable(compaction_events compaction_events.cpp)
60+
target_link_libraries(compaction_events PRIVATE copilot_sdk_cpp)
61+
set_target_properties(compaction_events PROPERTIES FOLDER "Examples")
62+
63+
# List models with vision capabilities example
64+
add_executable(list_models list_models.cpp)
65+
target_link_libraries(list_models PRIVATE copilot_sdk_cpp)
66+
set_target_properties(list_models PROPERTIES FOLDER "Examples")
67+
68+
# Tool execution progress monitoring example
69+
add_executable(tool_progress tool_progress.cpp)
70+
target_link_libraries(tool_progress PRIVATE copilot_sdk_cpp)
71+
set_target_properties(tool_progress PROPERTIES FOLDER "Examples")
72+
73+
# Hooks system example (v0.1.23)
74+
add_executable(hooks hooks.cpp)
75+
target_link_libraries(hooks PRIVATE copilot_sdk_cpp)
76+
set_target_properties(hooks PROPERTIES FOLDER "Examples")
77+
78+
# User input handler example (v0.1.23)
79+
add_executable(user_input user_input.cpp)
80+
target_link_libraries(user_input PRIVATE copilot_sdk_cpp)
81+
set_target_properties(user_input PROPERTIES FOLDER "Examples")
82+
83+
# Reasoning effort example (v0.1.23)
84+
add_executable(reasoning_effort reasoning_effort.cpp)
85+
target_link_libraries(reasoning_effort PRIVATE copilot_sdk_cpp)
86+
set_target_properties(reasoning_effort PROPERTIES FOLDER "Examples")

examples/compaction_events.cpp

Lines changed: 179 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,179 @@
1+
// Copyright (c) 2025 Elias Bachaalany
2+
// SPDX-License-Identifier: MIT
3+
4+
/// @file compaction_events.cpp
5+
/// @brief Example demonstrating session compaction and usage monitoring
6+
///
7+
/// This example shows how to:
8+
/// 1. Configure infinite sessions with a low compaction threshold
9+
/// 2. Subscribe to SessionCompactionStart, SessionCompactionComplete events
10+
/// 3. Monitor context usage via SessionUsageInfo events
11+
/// 4. Track compaction progress in real-time
12+
13+
#include <atomic>
14+
#include <chrono>
15+
#include <condition_variable>
16+
#include <copilot/copilot.hpp>
17+
#include <iostream>
18+
#include <mutex>
19+
#include <string>
20+
21+
int main()
22+
{
23+
try
24+
{
25+
copilot::ClientOptions options;
26+
options.log_level = "info";
27+
28+
copilot::Client client(options);
29+
30+
std::cout << "=== Compaction Events Example ===\n\n";
31+
std::cout << "This example monitors context compaction in real-time.\n";
32+
std::cout << "When the context window fills up, the session automatically\n";
33+
std::cout << "compacts (summarizes) older messages to free up space.\n\n";
34+
35+
client.start().get();
36+
37+
// Configure session with a low compaction threshold to trigger compaction sooner
38+
copilot::SessionConfig config;
39+
config.streaming = true;
40+
41+
copilot::InfiniteSessionConfig infinite_config;
42+
infinite_config.enabled = true;
43+
infinite_config.background_compaction_threshold = 0.10; // Trigger at 10% usage
44+
config.infinite_sessions = infinite_config;
45+
46+
auto session = client.create_session(config).get();
47+
std::cout << "Session created with low compaction threshold (10%)\n";
48+
std::cout << "Compaction events will appear when context usage exceeds the threshold.\n\n";
49+
50+
// Synchronization
51+
std::mutex mtx;
52+
std::condition_variable cv;
53+
std::atomic<bool> idle{false};
54+
55+
// Subscribe to all events, highlighting compaction-related ones
56+
auto subscription = session->on(
57+
[&](const copilot::SessionEvent& event)
58+
{
59+
if (auto* delta = event.try_as<copilot::AssistantMessageDeltaData>())
60+
{
61+
std::cout << delta->delta_content << std::flush;
62+
}
63+
else if (auto* msg = event.try_as<copilot::AssistantMessageData>())
64+
{
65+
// Final message in non-streaming mode
66+
if (!msg->content.empty())
67+
std::cout << "\nAssistant: " << msg->content << "\n";
68+
}
69+
else if (auto* usage = event.try_as<copilot::SessionUsageInfoData>())
70+
{
71+
// Context usage statistics
72+
double pct = (usage->token_limit > 0)
73+
? (usage->current_tokens / usage->token_limit * 100.0)
74+
: 0.0;
75+
std::cout << "\n[Usage] Tokens: "
76+
<< static_cast<int>(usage->current_tokens)
77+
<< "/" << static_cast<int>(usage->token_limit)
78+
<< " (" << static_cast<int>(pct) << "%)"
79+
<< " Messages: " << static_cast<int>(usage->messages_length)
80+
<< "\n";
81+
}
82+
else if (event.try_as<copilot::SessionCompactionStartData>())
83+
{
84+
std::cout << "\n*** COMPACTION STARTED ***\n"
85+
<< " Context is being summarized to free up space...\n";
86+
}
87+
else if (auto* complete = event.try_as<copilot::SessionCompactionCompleteData>())
88+
{
89+
std::cout << "\n*** COMPACTION COMPLETE ***\n";
90+
std::cout << " Success: " << (complete->success ? "yes" : "no") << "\n";
91+
92+
if (complete->error)
93+
std::cout << " Error: " << *complete->error << "\n";
94+
95+
if (complete->pre_compaction_tokens && complete->post_compaction_tokens)
96+
{
97+
std::cout << " Tokens: "
98+
<< static_cast<int>(*complete->pre_compaction_tokens)
99+
<< " -> "
100+
<< static_cast<int>(*complete->post_compaction_tokens)
101+
<< "\n";
102+
}
103+
104+
if (complete->pre_compaction_messages_length
105+
&& complete->post_compaction_messages_length)
106+
{
107+
std::cout << " Messages: "
108+
<< static_cast<int>(*complete->pre_compaction_messages_length)
109+
<< " -> "
110+
<< static_cast<int>(*complete->post_compaction_messages_length)
111+
<< "\n";
112+
}
113+
114+
if (complete->compaction_tokens_used)
115+
{
116+
auto& tokens = *complete->compaction_tokens_used;
117+
std::cout << " Compaction cost: in="
118+
<< static_cast<int>(tokens.input)
119+
<< " out=" << static_cast<int>(tokens.output)
120+
<< " cached=" << static_cast<int>(tokens.cached_input)
121+
<< "\n";
122+
}
123+
}
124+
else if (auto* error = event.try_as<copilot::SessionErrorData>())
125+
{
126+
std::cerr << "\nError: " << error->message << "\n";
127+
}
128+
else if (event.type == copilot::SessionEventType::SessionIdle)
129+
{
130+
std::lock_guard<std::mutex> lock(mtx);
131+
idle = true;
132+
cv.notify_one();
133+
}
134+
}
135+
);
136+
137+
// Interactive chat
138+
std::cout << "Send multiple messages to build up context and trigger compaction.\n";
139+
std::cout << "Type 'quit' to exit.\n\n> ";
140+
141+
std::string line;
142+
while (std::getline(std::cin, line))
143+
{
144+
if (line == "quit" || line == "exit")
145+
break;
146+
147+
if (line.empty())
148+
{
149+
std::cout << "> ";
150+
continue;
151+
}
152+
153+
idle = false;
154+
155+
copilot::MessageOptions msg_opts;
156+
msg_opts.prompt = line;
157+
session->send(msg_opts).get();
158+
159+
{
160+
std::unique_lock<std::mutex> lock(mtx);
161+
cv.wait(lock, [&idle]() { return idle.load(); });
162+
}
163+
164+
std::cout << "\n> ";
165+
}
166+
167+
// Cleanup
168+
std::cout << "\nCleaning up...\n";
169+
session->destroy().get();
170+
client.stop().get();
171+
172+
return 0;
173+
}
174+
catch (const std::exception& e)
175+
{
176+
std::cerr << "Error: " << e.what() << "\n";
177+
return 1;
178+
}
179+
}

0 commit comments

Comments
 (0)