Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
7047c76
chronicle
vijayupadya Apr 8, 2026
f82d439
local and cloud store
vijayupadya Apr 8, 2026
bb17a1f
upload vscode events to remote store
vijayupadya Apr 9, 2026
f0ec5d2
few updates
vijayupadya Apr 9, 2026
9f14396
consent ui and settings
vijayupadya Apr 10, 2026
ace0f70
few optimizations
vijayupadya Apr 10, 2026
bdf017b
test fix
vijayupadya Apr 10, 2026
d90b9cc
feedback updates
vijayupadya Apr 10, 2026
337dad6
test fix
vijayupadya Apr 10, 2026
2b9d115
Merge remote-tracking branch 'origin/main' into vscode-copilot-chat/m…
vijayupadya Apr 10, 2026
68e4f88
check setting to enable cmd
vijayupadya Apr 10, 2026
6677ba5
fix test
vijayupadya Apr 10, 2026
9cd9af2
Settings update and tool update
vijayupadya Apr 13, 2026
09924de
command update
vijayupadya Apr 14, 2026
b871d4f
Settings update
vijayupadya Apr 15, 2026
2326443
merge main
vijayupadya Apr 15, 2026
17f7f0e
Merge remote-tracking branch 'origin/main' into vscode-copilot-chat/m…
vijayupadya Apr 15, 2026
523aa86
feedback updates
vijayupadya Apr 15, 2026
485e4d0
setting and test update
vijayupadya Apr 15, 2026
adcd178
few updates
vijayupadya Apr 16, 2026
43bac33
updates
vijayupadya Apr 16, 2026
52503de
Merge remote-tracking branch 'origin/main' into vscode-copilot-chat/m…
vijayupadya Apr 16, 2026
4fac271
test update
vijayupadya Apr 16, 2026
1758933
blocks ci update
vijayupadya Apr 16, 2026
828f19f
feedback updates
vijayupadya Apr 16, 2026
8793d85
Merge remote-tracking branch 'origin/main' into vscode-copilot-chat/m…
vijayupadya Apr 16, 2026
49efd0f
comment update
vijayupadya Apr 16, 2026
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
41 changes: 41 additions & 0 deletions extensions/copilot/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -1174,6 +1174,32 @@
"legacyToolReferenceFullNames": [
"editFiles"
]
},
{
"name": "copilot_sessionStoreSql",
"displayName": "Session Store SQL",
"toolReferenceName": "sessionStoreSql",
"userDescription": "Query your Copilot session history using SQL",
"modelDescription": "Execute read-only SQL queries against the global session store containing history from ALL past coding sessions. Use this proactively when the user asks about:\n- What they've worked on recently or in the past\n- Prior approaches to similar problems\n- Project history and file changes\n- Sessions linked to PRs, issues, or commits\n- Temporal queries ('what was I doing yesterday?')\n\nSupports SQLite SQL including JOINs, FTS5 MATCH queries, aggregations, and subqueries.\n\n**Only one query per call — do not combine multiple statements with semicolons.**\n\nSchema:\n- sessions — id, cwd, repository, branch, summary, created_at, updated_at\n- turns — session_id, turn_index, user_message, assistant_response, timestamp\n- session_files — session_id, file_path, tool_name (edit/create), turn_index\n- session_refs — session_id, ref_type (commit/pr/issue), ref_value, turn_index\n- search_index — FTS5 virtual table (content, session_id, source_type). Use WHERE search_index MATCH 'query' for full-text search.",
"tags": [],
"canBeReferencedInPrompt": false,
"inputSchema": {
"type": "object",
"properties": {
"query": {
"type": "string",
"description": "A single read-only SQL query to execute. Supports SELECT, WITH, JOINs, aggregations, and FTS5 MATCH. Only one statement per call — do not combine multiple queries with semicolons."
},
"description": {
"type": "string",
"description": "A 2-5 word summary of what this query does (e.g. 'Recent sessions overview', 'Find PR sessions')."
}
},
"required": [
"query",
"description"
]
}
}
],
"languageModelToolSets": [
Expand Down Expand Up @@ -1487,6 +1513,21 @@
"name": "compact",
"description": "%copilot.agent.compact.description%"
},
{
"name": "chronicle",
"description": "%copilot.chronicle.description%",
"when": "github.copilot.sessionSearch.enabled"
},
{
"name": "chronicle:standup",
"description": "%copilot.chronicle.standup.description%",
"when": "github.copilot.sessionSearch.enabled"
},
{
"name": "chronicle:tips",
"description": "%copilot.chronicle.tips.description%",
"when": "github.copilot.sessionSearch.enabled"
},
{
"name": "explain",
"description": "%copilot.workspace.explain.description%"
Expand Down
7 changes: 7 additions & 0 deletions extensions/copilot/package.nls.json
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,13 @@
"copilot.edits.description": "Edit files in your workspace",
"copilot.agent.description": "Edit files in your workspace in agent mode",
"copilot.agent.compact.description": "Free up context by compacting the conversation history. Optionally include extra instructions for compaction.",
"copilot.chronicle.description": "Session history tools and insights",
"copilot.chronicle.standup.description": "Generate a standup report from recent coding sessions",
"copilot.chronicle.tips.description": "Get personalized tips based on your Copilot usage patterns",
"github.copilot.config.sessionSearch.enabled": "Enable session search and /chronicle commands. This is a team-internal setting.",
"github.copilot.config.sessionSearch.localIndex.enabled": "Enable local session tracking. When enabled, Copilot tracks session data locally for /chronicle commands.",
"github.copilot.config.sessionSearch.cloudSync.enabled": "Enable cloud sync for session data. When enabled, session data is synced to your Copilot account for cross-device access.",
"github.copilot.config.sessionSearch.cloudSync.excludeRepositories": "Repository patterns to exclude from cloud sync. Use exact `owner/repo` names or glob patterns like `my-org/*`. Sessions from matching repos will only be stored locally.",
"copilot.workspace.explain.description": "Explain how the code in your active editor works",
"copilot.workspace.edit.description": "Edit files in your workspace",
"copilot.workspace.review.description": "Review the selected code in your active editor",
Expand Down
160 changes: 160 additions & 0 deletions extensions/copilot/src/extension/chronicle/common/circuitBreaker.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,160 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/

/**
* Circuit breaker states:
* - CLOSED: Normal operation, requests are allowed
* - OPEN: Circuit is tripped, requests fail immediately
* - HALF_OPEN: Testing if the service has recovered
*/
export const CircuitState = {
CLOSED: 'CLOSED',
OPEN: 'OPEN',
HALF_OPEN: 'HALF_OPEN',
} as const;

export type CircuitState = (typeof CircuitState)[keyof typeof CircuitState];

export interface CircuitBreakerOptions {
/** Number of consecutive failures before opening the circuit. */
failureThreshold: number;
/** Time in milliseconds before attempting to close the circuit. */
resetTimeoutMs: number;
/** Time in milliseconds before a probe request times out and allows another probe. */
probeTimeoutMs: number;
/** Maximum reset timeout after exponential backoff on failed probes. Defaults to resetTimeoutMs (no backoff). */
maxResetTimeoutMs?: number;
}

const DEFAULT_OPTIONS: CircuitBreakerOptions = {
failureThreshold: 5,
resetTimeoutMs: 1_000,
probeTimeoutMs: 30_000,
};

/**
* Circuit breaker implementation to prevent cascading failures.
*
* When multiple consecutive failures occur, the circuit "opens" and
* immediately rejects subsequent requests without attempting them.
* After a cooldown period, it allows a single "probe" request to test
* if the service has recovered.
*
* Ported from copilot-agent-runtime/src/helpers/circuit-breaker.ts.
*/
export class CircuitBreaker {
private state: CircuitState = CircuitState.CLOSED;
private failureCount: number = 0;
private lastFailureTime: number = 0;
private probeInFlight: boolean = false;
private probeStartTime: number = 0;
private readonly options: CircuitBreakerOptions;
/** Current reset timeout, increases via exponential backoff on failed probes. */
private currentResetTimeoutMs: number;

constructor(options: Partial<CircuitBreakerOptions> = {}) {
this.options = { ...DEFAULT_OPTIONS, ...options };
this.currentResetTimeoutMs = this.options.resetTimeoutMs;
}

/**
* Get the current state of the circuit breaker.
*/
getState(): CircuitState {
this.updateState();
return this.state;
}

/**
* Check if requests should be allowed through.
* In HALF_OPEN state, only one probe request is allowed at a time.
*/
canRequest(): boolean {
this.updateState();

switch (this.state) {
case CircuitState.CLOSED:
return true;
case CircuitState.HALF_OPEN:
// Check if probe has timed out — prevents permanent deadlock
if (this.probeInFlight) {
const probeElapsed = Date.now() - this.probeStartTime;
if (probeElapsed >= this.options.probeTimeoutMs) {
this.probeInFlight = false;
}
}
if (this.probeInFlight) {
return false;
}
this.probeInFlight = true;
this.probeStartTime = Date.now();
return true;
case CircuitState.OPEN:
return false;
}
}

/**
* Record a successful request. Resets failure count and closes the circuit.
*/
recordSuccess(): void {
this.failureCount = 0;
this.probeInFlight = false;
this.currentResetTimeoutMs = this.options.resetTimeoutMs;
this.state = CircuitState.CLOSED;
}

/**
* Record a failed request. May open the circuit if threshold is exceeded.
*/
recordFailure(): void {
const wasHalfOpen = this.state === CircuitState.HALF_OPEN;
this.failureCount++;
this.lastFailureTime = Date.now();
this.probeInFlight = false;

if (this.failureCount >= this.options.failureThreshold) {
this.state = CircuitState.OPEN;
}

// Exponential backoff: double the probe interval after each failed probe
if (wasHalfOpen && this.state === CircuitState.OPEN) {
const maxTimeout = this.options.maxResetTimeoutMs ?? this.options.resetTimeoutMs;
this.currentResetTimeoutMs = Math.min(this.currentResetTimeoutMs * 2, maxTimeout);
}
}

/**
* Get the number of consecutive failures.
*/
getFailureCount(): number {
return this.failureCount;
}

/**
* Force reset the circuit breaker to closed state.
*/
reset(): void {
this.state = CircuitState.CLOSED;
this.failureCount = 0;
this.lastFailureTime = 0;
this.probeInFlight = false;
this.probeStartTime = 0;
this.currentResetTimeoutMs = this.options.resetTimeoutMs;
}

/**
* Update state based on timeout — transitions from OPEN to HALF_OPEN
* after the reset timeout has elapsed.
*/
private updateState(): void {
if (this.state === CircuitState.OPEN) {
const elapsed = Date.now() - this.lastFailureTime;
if (elapsed >= this.currentResetTimeoutMs) {
this.state = CircuitState.HALF_OPEN;
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/

// ── Cloud session API types ─────────────────────────────────────────────────────

/**
* Resolved GitHub repository numeric IDs (from GitHub REST API).
*/
export interface RepoIdentifiers {
ownerId: number;
repoId: number;
}

/**
* GitHub repository context combining string names with resolved numeric IDs.
*/
export interface GitHubRepository {
owner: string;
repo: string;
repoIds: RepoIdentifiers;
}

/**
* Cloud session and task IDs for an active remote session.
*/
export interface CloudSessionIds {
cloudSessionId: string;
cloudTaskId: string;
}

/**
* Response from creating a cloud session.
*/
export interface CreateSessionResponse {
id: string;
task_id?: string;
agent_task_id?: string;
}

/**
* A cloud session.
*/
export interface CloudSession {
id: string;
state: string;
task_id?: string;
owner_id: number;
repo_id: number;
created_at: string;
updated_at: string;
}

// ── Session event types (CLI-compatible) ────────────────────────────────────────
// Event format compatible with the cloud session pipeline.

/**
* Base structure for all session events sent to the cloud.
*/
export interface SessionEvent {
/** Unique event identifier (UUID v4). */
id: string;
/** ISO 8601 timestamp when the event was created. */
timestamp: string;
/** ID of the chronologically preceding event, forming a linked chain. Null for the first event. */
parentId: string | null;
/** When true, the event is transient and not persisted. */
ephemeral?: boolean;
/** Event type discriminator. */
type: string;
/** Event-specific payload. */
data: Record<string, unknown>;
}

/**
* Working directory context schema for session.start events.
*/
export interface WorkingDirectoryContext {
cwd?: string;
repository?: string;
branch?: string;
headCommit?: string;
}

/** Reason why session creation failed. */
export type CreateSessionFailureReason = 'policy_blocked' | 'error';

/** Result of attempting to create a cloud session. */
export type CreateSessionResult =
| { ok: true; response: CreateSessionResponse }
| { ok: false; reason: CreateSessionFailureReason };
Loading
Loading