diff --git a/clients/dotnet/src/AgentHostProtocol.Abstractions/Generated/Commands.generated.cs b/clients/dotnet/src/AgentHostProtocol.Abstractions/Generated/Commands.generated.cs
index 0ef02d8b..92f17602 100644
--- a/clients/dotnet/src/AgentHostProtocol.Abstractions/Generated/Commands.generated.cs
+++ b/clients/dotnet/src/AgentHostProtocol.Abstractions/Generated/Commands.generated.cs
@@ -462,7 +462,10 @@ public sealed record SubscribeResult
///
/// After creation, the client should subscribe to the session URI to receive state
/// updates. The server also broadcasts a `root/sessionAdded` notification to all
-/// clients.
+/// clients.
+///
+/// Repository preparation MUST finish before `session/ready` or executing turns.
+/// Clients recover the outcome from session state, not progress notifications.
public sealed record CreateSessionParams
{
/// Session URI (client-chosen, e.g. `ahp-session:/<uuid>`)
@@ -490,12 +493,24 @@ public sealed record CreateSessionParams
/// {@link AgentCapabilities.multipleWorkingDirectories}; a server without that
/// capability treats only the first entry as the session's working directory
/// and ignores the rest. Dispatch working-directory actions to change the set
- /// after the session has started.
+ /// after the session has started.
+ ///
+ /// A non-empty list and `repositorySource` are mutually exclusive.
+ /// A repository URI identifies the source, not a working-directory URI; one
+ /// source may produce multiple directories.
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
public List? WorkingDirectories { get; init; }
- /// Agent-specific configuration values collected via `resolveSessionConfig`.
- /// Keys and values correspond to the schema returned by the server.
+ /// Credential-free source to prepare; requires the agent's repositorySource capability.
+ [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
+ public string? RepositorySource { get; init; }
+
+ /// Requested branch, tag, or commit; requires a source and the capability's revision option.
+ [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
+ public string? RepositoryRevision { get; init; }
+
+ /// Session configuration values collected via `resolveSessionConfig`.
+ /// Keys and values follow the advertised {@link SessionConfigSchema}.
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
public Dictionary? Config { get; init; }
@@ -524,7 +539,10 @@ public sealed record CreateSessionParams
/// Disposes a session and cleans up server-side resources.
///
-/// The server broadcasts a `root/sessionRemoved` notification to all clients.
+/// The server broadcasts a `root/sessionRemoved` notification to all clients.
+/// Disposal MUST NOT erase a shared checkout or uncommitted user changes.
+/// Repository cleanup remains host-owned; ending a client's wait or subscription
+/// does not grant permission to delete repository data.
public sealed record DisposeSessionParams
{
/// Channel URI this command targets.
@@ -1346,7 +1364,10 @@ public sealed record DisposeTerminalParams
/// The client calls this command whenever the user changes a significant input
/// (e.g. picks a working directory, toggles a property). Each response returns
/// the full current property set (not a delta). The returned `values` contain
-/// server-resolved defaults to pass to `createSession`.
+/// server-resolved defaults to pass to `createSession`.
+///
+/// This command MUST NOT clone or prepare a repository. Repository context
+/// requires the agent's `repositorySource` capability.
public sealed record ResolveSessionConfigParams
{
public required string Channel { get; init; }
@@ -1365,7 +1386,15 @@ public sealed record ResolveSessionConfigParams
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
public string? WorkingDirectory { get; init; }
- /// Current user-filled configuration values
+ /// Credential-free source context; not a working-directory URI.
+ [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
+ public string? RepositorySource { get; init; }
+
+ /// Requested revision; requires a source and the capability's revision option.
+ [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
+ public string? RepositoryRevision { get; init; }
+
+ /// Current user-filled configuration values; see {@link SessionConfigSchema}.
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
public Dictionary? Config { get; init; }
}
@@ -1403,6 +1432,14 @@ public sealed record SessionConfigCompletionsParams
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
public string? WorkingDirectory { get; init; }
+ /// Repository context for configuration completions; this MUST NOT prepare a checkout.
+ [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
+ public string? RepositorySource { get; init; }
+
+ /// Requested revision; requires a source and the capability's revision option.
+ [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
+ public string? RepositoryRevision { get; init; }
+
/// Current user-filled configuration values (provides context for the query)
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
public Dictionary? Config { get; init; }
diff --git a/clients/dotnet/src/AgentHostProtocol.Abstractions/Generated/JsonSerializerContext.generated.cs b/clients/dotnet/src/AgentHostProtocol.Abstractions/Generated/JsonSerializerContext.generated.cs
index 9b164021..0283d2b0 100644
--- a/clients/dotnet/src/AgentHostProtocol.Abstractions/Generated/JsonSerializerContext.generated.cs
+++ b/clients/dotnet/src/AgentHostProtocol.Abstractions/Generated/JsonSerializerContext.generated.cs
@@ -264,6 +264,7 @@ namespace Microsoft.AgentHostProtocol;
[JsonSerializable(typeof(ReconnectResult))]
[JsonSerializable(typeof(ReconnectResultType))]
[JsonSerializable(typeof(ReconnectSnapshotResult))]
+[JsonSerializable(typeof(RepositorySourceCapability))]
[JsonSerializable(typeof(ResolveSessionConfigParams))]
[JsonSerializable(typeof(ResolveSessionConfigResult))]
[JsonSerializable(typeof(ResourceChange))]
diff --git a/clients/dotnet/src/AgentHostProtocol.Abstractions/Generated/Notifications.generated.cs b/clients/dotnet/src/AgentHostProtocol.Abstractions/Generated/Notifications.generated.cs
index 9b5bd1a1..b50e8946 100644
--- a/clients/dotnet/src/AgentHostProtocol.Abstractions/Generated/Notifications.generated.cs
+++ b/clients/dotnet/src/AgentHostProtocol.Abstractions/Generated/Notifications.generated.cs
@@ -117,7 +117,9 @@ public sealed record SessionSummaryChangedParams
/// the client then never shows an indicator.
/// - Like all notifications this is ephemeral and is **not** replayed on
/// reconnect. A client that never receives the terminal frame SHOULD expire
-/// the indicator after an idle timeout.
+/// the indicator after an idle timeout.
+/// - Completion of reported work does not establish session readiness.
+/// Observe session lifecycle state for the durable outcome.
public sealed record ProgressParams
{
/// Channel URI this notification belongs to (the root channel).
@@ -267,6 +269,14 @@ public sealed record PartialSessionSummary
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
public List? WorkingDirectories { get; init; }
+ /// Immutable requested source, separate from the host-resolved working directories.
+ [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
+ public string? RepositorySource { get; init; }
+
+ /// Immutable requested revision, not the checkout's current HEAD.
+ [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
+ public string? RepositoryRevision { get; init; }
+
/// Lightweight summary of this session's inline annotations channel
/// (`ahp-session:/<uuid>/annotations`). Surfaced so badge UI can render
/// annotation / entry counts without subscribing. Absent when the session
diff --git a/clients/dotnet/src/AgentHostProtocol.Abstractions/Generated/State.generated.cs b/clients/dotnet/src/AgentHostProtocol.Abstractions/Generated/State.generated.cs
index 5ead9d4c..1e923590 100644
--- a/clients/dotnet/src/AgentHostProtocol.Abstractions/Generated/State.generated.cs
+++ b/clients/dotnet/src/AgentHostProtocol.Abstractions/Generated/State.generated.cs
@@ -861,6 +861,10 @@ public sealed record AgentInfo
/// per-capability options.
public sealed record AgentCapabilities
{
+ /// The host accepts typed repository inputs for session creation and configuration queries.
+ [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
+ public RepositorySourceCapability? RepositorySource { get; init; }
+
/// The agent can host more than one concurrent chat per session. When absent,
/// clients MUST NOT call `createChat` to open chats beyond the default one the
/// session starts with. An empty object `{}` advertises multi-chat without
@@ -940,6 +944,14 @@ public sealed record MultipleWorkingDirectoriesCapability
public bool? PrimaryReplacement { get; init; }
}
+/// Options for repository-backed session creation.
+public sealed record RepositorySourceCapability
+{
+ /// When true, clients may supply an explicit repositoryRevision.
+ [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
+ public bool? Revision { get; init; }
+}
+
public sealed record SessionModelInfo
{
/// Model identifier
@@ -1547,6 +1559,14 @@ public sealed class SessionState
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
public List? WorkingDirectories { get; set; }
+ /// Immutable requested source, separate from the host-resolved working directories.
+ [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
+ public string? RepositorySource { get; set; }
+
+ /// Immutable requested revision, not the checkout's current HEAD.
+ [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
+ public string? RepositoryRevision { get; set; }
+
/// Lightweight summary of this session's inline annotations channel
/// (`ahp-session:/<uuid>/annotations`). Surfaced so badge UI can render
/// annotation / entry counts without subscribing. Absent when the session
@@ -1586,7 +1606,7 @@ public sealed class SessionState
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
public string? DefaultChat { get; set; }
- /// Session configuration schema and current values
+ /// Provider-specific session configuration schema and current values.
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
public SessionConfigState? Config { get; set; }
@@ -1882,6 +1902,14 @@ public sealed class SessionSummary
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
public List? WorkingDirectories { get; set; }
+ /// Immutable requested source, separate from the host-resolved working directories.
+ [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
+ public string? RepositorySource { get; set; }
+
+ /// Immutable requested revision, not the checkout's current HEAD.
+ [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
+ public string? RepositoryRevision { get; set; }
+
/// Lightweight summary of this session's inline annotations channel
/// (`ahp-session:/<uuid>/annotations`). Surfaced so badge UI can render
/// annotation / entry counts without subscribing. Absent when the session
diff --git a/clients/go/ahptypes/commands.generated.go b/clients/go/ahptypes/commands.generated.go
index 9ef81b6f..915981df 100644
--- a/clients/go/ahptypes/commands.generated.go
+++ b/clients/go/ahptypes/commands.generated.go
@@ -375,6 +375,9 @@ type SubscribeResult struct {
// After creation, the client should subscribe to the session URI to receive state
// updates. The server also broadcasts a `root/sessionAdded` notification to all
// clients.
+//
+// Repository preparation MUST finish before `session/ready` or executing turns.
+// Clients recover the outcome from session state, not progress notifications.
type CreateSessionParams struct {
// Channel URI this command targets.
Channel URI `json:"channel"`
@@ -396,9 +399,17 @@ type CreateSessionParams struct {
// capability treats only the first entry as the session's working directory
// and ignores the rest. Dispatch working-directory actions to change the set
// after the session has started.
+ //
+ // A non-empty list and `repositorySource` are mutually exclusive.
+ // A repository URI identifies the source, not a working-directory URI; one
+ // source may produce multiple directories.
WorkingDirectories []URI `json:"workingDirectories,omitempty"`
- // Agent-specific configuration values collected via `resolveSessionConfig`.
- // Keys and values correspond to the schema returned by the server.
+ // Credential-free source to prepare; requires the agent's repositorySource capability.
+ RepositorySource *URI `json:"repositorySource,omitempty"`
+ // Requested branch, tag, or commit; requires a source and the capability's revision option.
+ RepositoryRevision *string `json:"repositoryRevision,omitempty"`
+ // Session configuration values collected via `resolveSessionConfig`.
+ // Keys and values follow the advertised {@link SessionConfigSchema}.
Config map[string]json.RawMessage `json:"config,omitempty"`
// Eagerly claim an active client role for the new session.
//
@@ -423,6 +434,9 @@ type CreateSessionParams struct {
// Disposes a session and cleans up server-side resources.
//
// The server broadcasts a `root/sessionRemoved` notification to all clients.
+// Disposal MUST NOT erase a shared checkout or uncommitted user changes.
+// Repository cleanup remains host-owned; ending a client's wait or subscription
+// does not grant permission to delete repository data.
type DisposeSessionParams struct {
// Channel URI this command targets.
Channel URI `json:"channel"`
@@ -1070,6 +1084,9 @@ type DisposeTerminalParams struct {
// (e.g. picks a working directory, toggles a property). Each response returns
// the full current property set (not a delta). The returned `values` contain
// server-resolved defaults to pass to `createSession`.
+//
+// This command MUST NOT clone or prepare a repository. Repository context
+// requires the agent's `repositorySource` capability.
type ResolveSessionConfigParams struct {
// Channel URI this command targets.
Channel URI `json:"channel"`
@@ -1080,7 +1097,11 @@ type ResolveSessionConfigParams struct {
Provider *string `json:"provider,omitempty"`
// Working directory for the session
WorkingDirectory *URI `json:"workingDirectory,omitempty"`
- // Current user-filled configuration values
+ // Credential-free source context; not a working-directory URI.
+ RepositorySource *URI `json:"repositorySource,omitempty"`
+ // Requested revision; requires a source and the capability's revision option.
+ RepositoryRevision *string `json:"repositoryRevision,omitempty"`
+ // Current user-filled configuration values; see {@link SessionConfigSchema}.
Config map[string]json.RawMessage `json:"config,omitempty"`
}
@@ -1107,6 +1128,10 @@ type SessionConfigCompletionsParams struct {
Provider *string `json:"provider,omitempty"`
// Working directory for the session
WorkingDirectory *URI `json:"workingDirectory,omitempty"`
+ // Repository context for configuration completions; this MUST NOT prepare a checkout.
+ RepositorySource *URI `json:"repositorySource,omitempty"`
+ // Requested revision; requires a source and the capability's revision option.
+ RepositoryRevision *string `json:"repositoryRevision,omitempty"`
// Current user-filled configuration values (provides context for the query)
Config map[string]json.RawMessage `json:"config,omitempty"`
// Property id from the schema to query values for
diff --git a/clients/go/ahptypes/notifications.generated.go b/clients/go/ahptypes/notifications.generated.go
index 62db00de..32e4ee12 100644
--- a/clients/go/ahptypes/notifications.generated.go
+++ b/clients/go/ahptypes/notifications.generated.go
@@ -115,6 +115,8 @@ type SessionSummaryChangedParams struct {
// - Like all notifications this is ephemeral and is **not** replayed on
// reconnect. A client that never receives the terminal frame SHOULD expire
// the indicator after an idle timeout.
+// - Completion of reported work does not establish session readiness.
+// Observe session lifecycle state for the durable outcome.
type ProgressParams struct {
// Channel URI this notification belongs to (the root channel).
Channel URI `json:"channel"`
@@ -232,6 +234,10 @@ type PartialSessionSummary struct {
// {@link ChatSummary.workingDirectories | their own `workingDirectories`}; a
// chat that sets none operates against this full set.
WorkingDirectories []URI `json:"workingDirectories,omitempty"`
+ // Immutable requested source, separate from the host-resolved working directories.
+ RepositorySource *URI `json:"repositorySource,omitempty"`
+ // Immutable requested revision, not the checkout's current HEAD.
+ RepositoryRevision *string `json:"repositoryRevision,omitempty"`
// Lightweight summary of this session's inline annotations channel
// (`ahp-session://annotations`). Surfaced so badge UI can render
// annotation / entry counts without subscribing. Absent when the session
diff --git a/clients/go/ahptypes/state.generated.go b/clients/go/ahptypes/state.generated.go
index 269b50f5..b438323c 100644
--- a/clients/go/ahptypes/state.generated.go
+++ b/clients/go/ahptypes/state.generated.go
@@ -676,6 +676,8 @@ type AgentInfo struct {
// corresponding client commands MUST NOT be used. Sub-fields carry
// per-capability options.
type AgentCapabilities struct {
+ // The host accepts typed repository inputs for session creation and configuration queries.
+ RepositorySource *RepositorySourceCapability `json:"repositorySource,omitempty"`
// The agent can host more than one concurrent chat per session. When absent,
// clients MUST NOT call `createChat` to open chats beyond the default one the
// session starts with. An empty object `{}` advertises multi-chat without
@@ -744,6 +746,12 @@ type MultipleWorkingDirectoriesCapability struct {
PrimaryReplacement *bool `json:"primaryReplacement,omitempty"`
}
+// Options for repository-backed session creation.
+type RepositorySourceCapability struct {
+ // When true, clients may supply an explicit repositoryRevision.
+ Revision *bool `json:"revision,omitempty"`
+}
+
type SessionModelInfo struct {
// Model identifier
Id string `json:"id"`
@@ -877,6 +885,10 @@ type SessionState struct {
// {@link ChatSummary.workingDirectories | their own `workingDirectories`}; a
// chat that sets none operates against this full set.
WorkingDirectories []URI `json:"workingDirectories,omitempty"`
+ // Immutable requested source, separate from the host-resolved working directories.
+ RepositorySource *URI `json:"repositorySource,omitempty"`
+ // Immutable requested revision, not the checkout's current HEAD.
+ RepositoryRevision *string `json:"repositoryRevision,omitempty"`
// Lightweight summary of this session's inline annotations channel
// (`ahp-session://annotations`). Surfaced so badge UI can render
// annotation / entry counts without subscribing. Absent when the session
@@ -905,7 +917,7 @@ type SessionState struct {
// marker — chats remain equal peers at the protocol level. Hosts MAY change
// this over the session's lifetime.
DefaultChat *URI `json:"defaultChat,omitempty"`
- // Session configuration schema and current values
+ // Provider-specific session configuration schema and current values.
Config *SessionConfigState `json:"config,omitempty"`
// Top-level customizations active in this session.
//
@@ -1153,6 +1165,10 @@ type SessionSummary struct {
// {@link ChatSummary.workingDirectories | their own `workingDirectories`}; a
// chat that sets none operates against this full set.
WorkingDirectories []URI `json:"workingDirectories,omitempty"`
+ // Immutable requested source, separate from the host-resolved working directories.
+ RepositorySource *URI `json:"repositorySource,omitempty"`
+ // Immutable requested revision, not the checkout's current HEAD.
+ RepositoryRevision *string `json:"repositoryRevision,omitempty"`
// Lightweight summary of this session's inline annotations channel
// (`ahp-session://annotations`). Surfaced so badge UI can render
// annotation / entry counts without subscribing. Absent when the session
diff --git a/clients/kotlin/src/main/kotlin/com/microsoft/agenthostprotocol/generated/Commands.generated.kt b/clients/kotlin/src/main/kotlin/com/microsoft/agenthostprotocol/generated/Commands.generated.kt
index 01fb0e15..65a61d25 100644
--- a/clients/kotlin/src/main/kotlin/com/microsoft/agenthostprotocol/generated/Commands.generated.kt
+++ b/clients/kotlin/src/main/kotlin/com/microsoft/agenthostprotocol/generated/Commands.generated.kt
@@ -617,11 +617,23 @@ data class CreateSessionParams(
* capability treats only the first entry as the session's working directory
* and ignores the rest. Dispatch working-directory actions to change the set
* after the session has started.
+ *
+ * A non-empty list and `repositorySource` are mutually exclusive.
+ * A repository URI identifies the source, not a working-directory URI; one
+ * source may produce multiple directories.
*/
val workingDirectories: List? = null,
/**
- * Agent-specific configuration values collected via `resolveSessionConfig`.
- * Keys and values correspond to the schema returned by the server.
+ * Credential-free source to prepare; requires the agent's repositorySource capability.
+ */
+ val repositorySource: String? = null,
+ /**
+ * Requested branch, tag, or commit; requires a source and the capability's revision option.
+ */
+ val repositoryRevision: String? = null,
+ /**
+ * Session configuration values collected via `resolveSessionConfig`.
+ * Keys and values follow the advertised {@link SessionConfigSchema}.
*/
val config: Map? = null,
/**
@@ -1317,7 +1329,15 @@ data class ResolveSessionConfigParams(
*/
val workingDirectory: String? = null,
/**
- * Current user-filled configuration values
+ * Credential-free source context; not a working-directory URI.
+ */
+ val repositorySource: String? = null,
+ /**
+ * Requested revision; requires a source and the capability's revision option.
+ */
+ val repositoryRevision: String? = null,
+ /**
+ * Current user-filled configuration values; see {@link SessionConfigSchema}.
*/
val config: Map? = null
)
@@ -1433,6 +1453,14 @@ data class SessionConfigCompletionsParams(
* Working directory for the session
*/
val workingDirectory: String? = null,
+ /**
+ * Repository context for configuration completions; this MUST NOT prepare a checkout.
+ */
+ val repositorySource: String? = null,
+ /**
+ * Requested revision; requires a source and the capability's revision option.
+ */
+ val repositoryRevision: String? = null,
/**
* Current user-filled configuration values (provides context for the query)
*/
diff --git a/clients/kotlin/src/main/kotlin/com/microsoft/agenthostprotocol/generated/Notifications.generated.kt b/clients/kotlin/src/main/kotlin/com/microsoft/agenthostprotocol/generated/Notifications.generated.kt
index 21d0f0ff..e6c26226 100644
--- a/clients/kotlin/src/main/kotlin/com/microsoft/agenthostprotocol/generated/Notifications.generated.kt
+++ b/clients/kotlin/src/main/kotlin/com/microsoft/agenthostprotocol/generated/Notifications.generated.kt
@@ -225,6 +225,14 @@ data class PartialSessionSummary(
* chat that sets none operates against this full set.
*/
val workingDirectories: List? = null,
+ /**
+ * Immutable requested source, separate from the host-resolved working directories.
+ */
+ val repositorySource: String? = null,
+ /**
+ * Immutable requested revision, not the checkout's current HEAD.
+ */
+ val repositoryRevision: String? = null,
/**
* Lightweight summary of this session's inline annotations channel
* (`ahp-session://annotations`). Surfaced so badge UI can render
diff --git a/clients/kotlin/src/main/kotlin/com/microsoft/agenthostprotocol/generated/State.generated.kt b/clients/kotlin/src/main/kotlin/com/microsoft/agenthostprotocol/generated/State.generated.kt
index 9fee270b..dc1cf07d 100644
--- a/clients/kotlin/src/main/kotlin/com/microsoft/agenthostprotocol/generated/State.generated.kt
+++ b/clients/kotlin/src/main/kotlin/com/microsoft/agenthostprotocol/generated/State.generated.kt
@@ -1335,6 +1335,10 @@ data class AgentInfo(
@Serializable
data class AgentCapabilities(
+ /**
+ * The host accepts typed repository inputs for session creation and configuration queries.
+ */
+ val repositorySource: RepositorySourceCapability? = null,
/**
* The agent can host more than one concurrent chat per session. When absent,
* clients MUST NOT call `createChat` to open chats beyond the default one the
@@ -1415,6 +1419,14 @@ data class MultipleWorkingDirectoriesCapability(
val primaryReplacement: Boolean? = null
)
+@Serializable
+data class RepositorySourceCapability(
+ /**
+ * When true, clients may supply an explicit repositoryRevision.
+ */
+ val revision: Boolean? = null
+)
+
@Serializable
data class SessionModelInfo(
/**
@@ -1759,6 +1771,14 @@ data class SessionState(
* chat that sets none operates against this full set.
*/
val workingDirectories: List? = null,
+ /**
+ * Immutable requested source, separate from the host-resolved working directories.
+ */
+ val repositorySource: String? = null,
+ /**
+ * Immutable requested revision, not the checkout's current HEAD.
+ */
+ val repositoryRevision: String? = null,
/**
* Lightweight summary of this session's inline annotations channel
* (`ahp-session://annotations`). Surfaced so badge UI can render
@@ -1802,7 +1822,7 @@ data class SessionState(
*/
val defaultChat: String? = null,
/**
- * Session configuration schema and current values
+ * Provider-specific session configuration schema and current values.
*/
val config: SessionConfigState? = null,
/**
@@ -2040,6 +2060,14 @@ data class SessionSummary(
* chat that sets none operates against this full set.
*/
val workingDirectories: List? = null,
+ /**
+ * Immutable requested source, separate from the host-resolved working directories.
+ */
+ val repositorySource: String? = null,
+ /**
+ * Immutable requested revision, not the checkout's current HEAD.
+ */
+ val repositoryRevision: String? = null,
/**
* Lightweight summary of this session's inline annotations channel
* (`ahp-session://annotations`). Surfaced so badge UI can render
diff --git a/clients/rust/crates/ahp-types/src/commands.rs b/clients/rust/crates/ahp-types/src/commands.rs
index e3dfe255..049601cb 100644
--- a/clients/rust/crates/ahp-types/src/commands.rs
+++ b/clients/rust/crates/ahp-types/src/commands.rs
@@ -558,6 +558,9 @@ pub struct SubscribeResult {
/// After creation, the client should subscribe to the session URI to receive state
/// updates. The server also broadcasts a `root/sessionAdded` notification to all
/// clients.
+///
+/// Repository preparation MUST finish before `session/ready` or executing turns.
+/// Clients recover the outcome from session state, not progress notifications.
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct CreateSessionParams {
@@ -583,10 +586,20 @@ pub struct CreateSessionParams {
/// capability treats only the first entry as the session's working directory
/// and ignores the rest. Dispatch working-directory actions to change the set
/// after the session has started.
+ ///
+ /// A non-empty list and `repositorySource` are mutually exclusive.
+ /// A repository URI identifies the source, not a working-directory URI; one
+ /// source may produce multiple directories.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub working_directories: Option>,
- /// Agent-specific configuration values collected via `resolveSessionConfig`.
- /// Keys and values correspond to the schema returned by the server.
+ /// Credential-free source to prepare; requires the agent's repositorySource capability.
+ #[serde(default, skip_serializing_if = "Option::is_none")]
+ pub repository_source: Option,
+ /// Requested branch, tag, or commit; requires a source and the capability's revision option.
+ #[serde(default, skip_serializing_if = "Option::is_none")]
+ pub repository_revision: Option,
+ /// Session configuration values collected via `resolveSessionConfig`.
+ /// Keys and values follow the advertised {@link SessionConfigSchema}.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub config: Option,
/// Eagerly claim an active client role for the new session.
@@ -614,6 +627,9 @@ pub struct CreateSessionParams {
/// Disposes a session and cleans up server-side resources.
///
/// The server broadcasts a `root/sessionRemoved` notification to all clients.
+/// Disposal MUST NOT erase a shared checkout or uncommitted user changes.
+/// Repository cleanup remains host-owned; ending a client's wait or subscription
+/// does not grant permission to delete repository data.
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct DisposeSessionParams {
@@ -1374,6 +1390,9 @@ pub struct DisposeTerminalParams {
/// (e.g. picks a working directory, toggles a property). Each response returns
/// the full current property set (not a delta). The returned `values` contain
/// server-resolved defaults to pass to `createSession`.
+///
+/// This command MUST NOT clone or prepare a repository. Repository context
+/// requires the agent's `repositorySource` capability.
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct ResolveSessionConfigParams {
@@ -1389,7 +1408,13 @@ pub struct ResolveSessionConfigParams {
/// Working directory for the session
#[serde(default, skip_serializing_if = "Option::is_none")]
pub working_directory: Option,
- /// Current user-filled configuration values
+ /// Credential-free source context; not a working-directory URI.
+ #[serde(default, skip_serializing_if = "Option::is_none")]
+ pub repository_source: Option,
+ /// Requested revision; requires a source and the capability's revision option.
+ #[serde(default, skip_serializing_if = "Option::is_none")]
+ pub repository_revision: Option,
+ /// Current user-filled configuration values; see {@link SessionConfigSchema}.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub config: Option,
}
@@ -1424,6 +1449,12 @@ pub struct SessionConfigCompletionsParams {
/// Working directory for the session
#[serde(default, skip_serializing_if = "Option::is_none")]
pub working_directory: Option,
+ /// Repository context for configuration completions; this MUST NOT prepare a checkout.
+ #[serde(default, skip_serializing_if = "Option::is_none")]
+ pub repository_source: Option,
+ /// Requested revision; requires a source and the capability's revision option.
+ #[serde(default, skip_serializing_if = "Option::is_none")]
+ pub repository_revision: Option,
/// Current user-filled configuration values (provides context for the query)
#[serde(default, skip_serializing_if = "Option::is_none")]
pub config: Option,
diff --git a/clients/rust/crates/ahp-types/src/notifications.rs b/clients/rust/crates/ahp-types/src/notifications.rs
index 3fa4cce5..d915b627 100644
--- a/clients/rust/crates/ahp-types/src/notifications.rs
+++ b/clients/rust/crates/ahp-types/src/notifications.rs
@@ -153,6 +153,8 @@ pub struct SessionSummaryChangedParams {
/// - Like all notifications this is ephemeral and is **not** replayed on
/// reconnect. A client that never receives the terminal frame SHOULD expire
/// the indicator after an idle timeout.
+/// - Completion of reported work does not establish session readiness.
+/// Observe session lifecycle state for the durable outcome.
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct ProgressParams {
@@ -292,6 +294,12 @@ pub struct PartialSessionSummary {
/// chat that sets none operates against this full set.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub working_directories: Option>,
+ /// Immutable requested source, separate from the host-resolved working directories.
+ #[serde(default, skip_serializing_if = "Option::is_none")]
+ pub repository_source: Option,
+ /// Immutable requested revision, not the checkout's current HEAD.
+ #[serde(default, skip_serializing_if = "Option::is_none")]
+ pub repository_revision: Option,
/// Lightweight summary of this session's inline annotations channel
/// (`ahp-session://annotations`). Surfaced so badge UI can render
/// annotation / entry counts without subscribing. Absent when the session
diff --git a/clients/rust/crates/ahp-types/src/state.rs b/clients/rust/crates/ahp-types/src/state.rs
index 6d7ff245..d5fd78ed 100644
--- a/clients/rust/crates/ahp-types/src/state.rs
+++ b/clients/rust/crates/ahp-types/src/state.rs
@@ -1623,6 +1623,9 @@ pub struct AgentInfo {
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Default)]
#[serde(rename_all = "camelCase")]
pub struct AgentCapabilities {
+ /// The host accepts typed repository inputs for session creation and configuration queries.
+ #[serde(default, skip_serializing_if = "Option::is_none")]
+ pub repository_source: Option,
/// The agent can host more than one concurrent chat per session. When absent,
/// clients MUST NOT call `createChat` to open chats beyond the default one the
/// session starts with. An empty object `{}` advertises multi-chat without
@@ -1701,6 +1704,15 @@ pub struct MultipleWorkingDirectoriesCapability {
pub primary_replacement: Option,
}
+/// Options for repository-backed session creation.
+#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Default)]
+#[serde(rename_all = "camelCase")]
+pub struct RepositorySourceCapability {
+ /// When true, clients may supply an explicit repositoryRevision.
+ #[serde(default, skip_serializing_if = "Option::is_none")]
+ pub revision: Option,
+}
+
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct SessionModelInfo {
@@ -2021,6 +2033,12 @@ pub struct SessionState {
/// chat that sets none operates against this full set.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub working_directories: Option>,
+ /// Immutable requested source, separate from the host-resolved working directories.
+ #[serde(default, skip_serializing_if = "Option::is_none")]
+ pub repository_source: Option,
+ /// Immutable requested revision, not the checkout's current HEAD.
+ #[serde(default, skip_serializing_if = "Option::is_none")]
+ pub repository_revision: Option,
/// Lightweight summary of this session's inline annotations channel
/// (`ahp-session://annotations`). Surfaced so badge UI can render
/// annotation / entry counts without subscribing. Absent when the session
@@ -2053,7 +2071,7 @@ pub struct SessionState {
/// this over the session's lifetime.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub default_chat: Option,
- /// Session configuration schema and current values
+ /// Provider-specific session configuration schema and current values.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub config: Option,
/// Top-level customizations active in this session.
@@ -2357,6 +2375,12 @@ pub struct SessionSummary {
/// chat that sets none operates against this full set.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub working_directories: Option>,
+ /// Immutable requested source, separate from the host-resolved working directories.
+ #[serde(default, skip_serializing_if = "Option::is_none")]
+ pub repository_source: Option,
+ /// Immutable requested revision, not the checkout's current HEAD.
+ #[serde(default, skip_serializing_if = "Option::is_none")]
+ pub repository_revision: Option,
/// Lightweight summary of this session's inline annotations channel
/// (`ahp-session://annotations`). Surfaced so badge UI can render
/// annotation / entry counts without subscribing. Absent when the session
diff --git a/clients/swift/AgentHostProtocol/Sources/AgentHostProtocol/Generated/Commands.generated.swift b/clients/swift/AgentHostProtocol/Sources/AgentHostProtocol/Generated/Commands.generated.swift
index 9d9ca3e2..fd5f6830 100644
--- a/clients/swift/AgentHostProtocol/Sources/AgentHostProtocol/Generated/Commands.generated.swift
+++ b/clients/swift/AgentHostProtocol/Sources/AgentHostProtocol/Generated/Commands.generated.swift
@@ -651,9 +651,17 @@ public struct CreateSessionParams: Codable, Sendable {
/// capability treats only the first entry as the session's working directory
/// and ignores the rest. Dispatch working-directory actions to change the set
/// after the session has started.
+ ///
+ /// A non-empty list and `repositorySource` are mutually exclusive.
+ /// A repository URI identifies the source, not a working-directory URI; one
+ /// source may produce multiple directories.
public var workingDirectories: [String]?
- /// Agent-specific configuration values collected via `resolveSessionConfig`.
- /// Keys and values correspond to the schema returned by the server.
+ /// Credential-free source to prepare; requires the agent's repositorySource capability.
+ public var repositorySource: String?
+ /// Requested branch, tag, or commit; requires a source and the capability's revision option.
+ public var repositoryRevision: String?
+ /// Session configuration values collected via `resolveSessionConfig`.
+ /// Keys and values follow the advertised {@link SessionConfigSchema}.
public var config: [String: AnyCodable]?
/// Eagerly claim an active client role for the new session.
///
@@ -679,6 +687,8 @@ public struct CreateSessionParams: Codable, Sendable {
case meta = "_meta"
case provider
case workingDirectories
+ case repositorySource
+ case repositoryRevision
case config
case activeClient
case progressToken
@@ -689,6 +699,8 @@ public struct CreateSessionParams: Codable, Sendable {
meta: [String: AnyCodable]? = nil,
provider: String? = nil,
workingDirectories: [String]? = nil,
+ repositorySource: String? = nil,
+ repositoryRevision: String? = nil,
config: [String: AnyCodable]? = nil,
activeClient: SessionActiveClient? = nil,
progressToken: String? = nil
@@ -697,6 +709,8 @@ public struct CreateSessionParams: Codable, Sendable {
self.meta = meta
self.provider = provider
self.workingDirectories = workingDirectories
+ self.repositorySource = repositorySource
+ self.repositoryRevision = repositoryRevision
self.config = config
self.activeClient = activeClient
self.progressToken = progressToken
@@ -1596,7 +1610,11 @@ public struct ResolveSessionConfigParams: Codable, Sendable {
public var provider: String?
/// Working directory for the session
public var workingDirectory: String?
- /// Current user-filled configuration values
+ /// Credential-free source context; not a working-directory URI.
+ public var repositorySource: String?
+ /// Requested revision; requires a source and the capability's revision option.
+ public var repositoryRevision: String?
+ /// Current user-filled configuration values; see {@link SessionConfigSchema}.
public var config: [String: AnyCodable]?
enum CodingKeys: String, CodingKey {
@@ -1604,6 +1622,8 @@ public struct ResolveSessionConfigParams: Codable, Sendable {
case meta = "_meta"
case provider
case workingDirectory
+ case repositorySource
+ case repositoryRevision
case config
}
@@ -1612,12 +1632,16 @@ public struct ResolveSessionConfigParams: Codable, Sendable {
meta: [String: AnyCodable]? = nil,
provider: String? = nil,
workingDirectory: String? = nil,
+ repositorySource: String? = nil,
+ repositoryRevision: String? = nil,
config: [String: AnyCodable]? = nil
) {
self.channel = channel
self.meta = meta
self.provider = provider
self.workingDirectory = workingDirectory
+ self.repositorySource = repositorySource
+ self.repositoryRevision = repositoryRevision
self.config = config
}
}
@@ -1749,6 +1773,10 @@ public struct SessionConfigCompletionsParams: Codable, Sendable {
public var provider: String?
/// Working directory for the session
public var workingDirectory: String?
+ /// Repository context for configuration completions; this MUST NOT prepare a checkout.
+ public var repositorySource: String?
+ /// Requested revision; requires a source and the capability's revision option.
+ public var repositoryRevision: String?
/// Current user-filled configuration values (provides context for the query)
public var config: [String: AnyCodable]?
/// Property id from the schema to query values for
@@ -1761,6 +1789,8 @@ public struct SessionConfigCompletionsParams: Codable, Sendable {
case meta = "_meta"
case provider
case workingDirectory
+ case repositorySource
+ case repositoryRevision
case config
case property
case query
@@ -1771,6 +1801,8 @@ public struct SessionConfigCompletionsParams: Codable, Sendable {
meta: [String: AnyCodable]? = nil,
provider: String? = nil,
workingDirectory: String? = nil,
+ repositorySource: String? = nil,
+ repositoryRevision: String? = nil,
config: [String: AnyCodable]? = nil,
property: String,
query: String? = nil
@@ -1779,6 +1811,8 @@ public struct SessionConfigCompletionsParams: Codable, Sendable {
self.meta = meta
self.provider = provider
self.workingDirectory = workingDirectory
+ self.repositorySource = repositorySource
+ self.repositoryRevision = repositoryRevision
self.config = config
self.property = property
self.query = query
diff --git a/clients/swift/AgentHostProtocol/Sources/AgentHostProtocol/Generated/Notifications.generated.swift b/clients/swift/AgentHostProtocol/Sources/AgentHostProtocol/Generated/Notifications.generated.swift
index f865befd..271f2ae1 100644
--- a/clients/swift/AgentHostProtocol/Sources/AgentHostProtocol/Generated/Notifications.generated.swift
+++ b/clients/swift/AgentHostProtocol/Sources/AgentHostProtocol/Generated/Notifications.generated.swift
@@ -217,6 +217,10 @@ public struct PartialSessionSummary: Codable, Sendable {
/// {@link ChatSummary.workingDirectories | their own `workingDirectories`}; a
/// chat that sets none operates against this full set.
public var workingDirectories: [String]?
+ /// Immutable requested source, separate from the host-resolved working directories.
+ public var repositorySource: String?
+ /// Immutable requested revision, not the checkout's current HEAD.
+ public var repositoryRevision: String?
/// Lightweight summary of this session's inline annotations channel
/// (`ahp-session://annotations`). Surfaced so badge UI can render
/// annotation / entry counts without subscribing. Absent when the session
@@ -247,6 +251,8 @@ public struct PartialSessionSummary: Codable, Sendable {
case origin
case project
case workingDirectories
+ case repositorySource
+ case repositoryRevision
case annotations
case resource
case createdAt
@@ -263,6 +269,8 @@ public struct PartialSessionSummary: Codable, Sendable {
origin: SessionOrigin? = nil,
project: ProjectInfo? = nil,
workingDirectories: [String]? = nil,
+ repositorySource: String? = nil,
+ repositoryRevision: String? = nil,
annotations: AnnotationsSummary? = nil,
resource: String? = nil,
createdAt: String? = nil,
@@ -277,6 +285,8 @@ public struct PartialSessionSummary: Codable, Sendable {
self.origin = origin
self.project = project
self.workingDirectories = workingDirectories
+ self.repositorySource = repositorySource
+ self.repositoryRevision = repositoryRevision
self.annotations = annotations
self.resource = resource
self.createdAt = createdAt
diff --git a/clients/swift/AgentHostProtocol/Sources/AgentHostProtocol/Generated/State.generated.swift b/clients/swift/AgentHostProtocol/Sources/AgentHostProtocol/Generated/State.generated.swift
index 313783d6..b5c031eb 100644
--- a/clients/swift/AgentHostProtocol/Sources/AgentHostProtocol/Generated/State.generated.swift
+++ b/clients/swift/AgentHostProtocol/Sources/AgentHostProtocol/Generated/State.generated.swift
@@ -1321,6 +1321,8 @@ public struct AgentInfo: Codable, Sendable {
}
public struct AgentCapabilities: Codable, Sendable {
+ /// The host accepts typed repository inputs for session creation and configuration queries.
+ public var repositorySource: RepositorySourceCapability?
/// The agent can host more than one concurrent chat per session. When absent,
/// clients MUST NOT call `createChat` to open chats beyond the default one the
/// session starts with. An empty object `{}` advertises multi-chat without
@@ -1338,9 +1340,11 @@ public struct AgentCapabilities: Codable, Sendable {
public var multipleWorkingDirectories: MultipleWorkingDirectoriesCapability?
public init(
+ repositorySource: RepositorySourceCapability? = nil,
multipleChats: MultipleChatsCapability? = nil,
multipleWorkingDirectories: MultipleWorkingDirectoriesCapability? = nil
) {
+ self.repositorySource = repositorySource
self.multipleChats = multipleChats
self.multipleWorkingDirectories = multipleWorkingDirectories
}
@@ -1411,6 +1415,17 @@ public struct MultipleWorkingDirectoriesCapability: Codable, Sendable {
}
}
+public struct RepositorySourceCapability: Codable, Sendable {
+ /// When true, clients may supply an explicit repositoryRevision.
+ public var revision: Bool?
+
+ public init(
+ revision: Bool? = nil
+ ) {
+ self.revision = revision
+ }
+}
+
public struct SessionModelInfo: Codable, Sendable {
/// Model identifier
public var id: String
@@ -1811,6 +1826,10 @@ public struct SessionState: Codable, Sendable {
/// {@link ChatSummary.workingDirectories | their own `workingDirectories`}; a
/// chat that sets none operates against this full set.
public var workingDirectories: [String]?
+ /// Immutable requested source, separate from the host-resolved working directories.
+ public var repositorySource: String?
+ /// Immutable requested revision, not the checkout's current HEAD.
+ public var repositoryRevision: String?
/// Lightweight summary of this session's inline annotations channel
/// (`ahp-session://annotations`). Surfaced so badge UI can render
/// annotation / entry counts without subscribing. Absent when the session
@@ -1839,7 +1858,7 @@ public struct SessionState: Codable, Sendable {
/// marker — chats remain equal peers at the protocol level. Hosts MAY change
/// this over the session's lifetime.
public var defaultChat: String?
- /// Session configuration schema and current values
+ /// Provider-specific session configuration schema and current values.
public var config: SessionConfigState?
/// Top-level customizations active in this session.
///
@@ -1900,6 +1919,8 @@ public struct SessionState: Codable, Sendable {
case origin
case project
case workingDirectories
+ case repositorySource
+ case repositoryRevision
case annotations
case lifecycle
case creationError
@@ -1922,6 +1943,8 @@ public struct SessionState: Codable, Sendable {
origin: SessionOrigin? = nil,
project: ProjectInfo? = nil,
workingDirectories: [String]? = nil,
+ repositorySource: String? = nil,
+ repositoryRevision: String? = nil,
annotations: AnnotationsSummary? = nil,
lifecycle: SessionLifecycle,
creationError: ErrorInfo? = nil,
@@ -1942,6 +1965,8 @@ public struct SessionState: Codable, Sendable {
self.origin = origin
self.project = project
self.workingDirectories = workingDirectories
+ self.repositorySource = repositorySource
+ self.repositoryRevision = repositoryRevision
self.annotations = annotations
self.lifecycle = lifecycle
self.creationError = creationError
@@ -2139,6 +2164,10 @@ public struct SessionSummary: Codable, Sendable {
/// {@link ChatSummary.workingDirectories | their own `workingDirectories`}; a
/// chat that sets none operates against this full set.
public var workingDirectories: [String]?
+ /// Immutable requested source, separate from the host-resolved working directories.
+ public var repositorySource: String?
+ /// Immutable requested revision, not the checkout's current HEAD.
+ public var repositoryRevision: String?
/// Lightweight summary of this session's inline annotations channel
/// (`ahp-session://annotations`). Surfaced so badge UI can render
/// annotation / entry counts without subscribing. Absent when the session
@@ -2169,6 +2198,8 @@ public struct SessionSummary: Codable, Sendable {
case origin
case project
case workingDirectories
+ case repositorySource
+ case repositoryRevision
case annotations
case resource
case createdAt
@@ -2185,6 +2216,8 @@ public struct SessionSummary: Codable, Sendable {
origin: SessionOrigin? = nil,
project: ProjectInfo? = nil,
workingDirectories: [String]? = nil,
+ repositorySource: String? = nil,
+ repositoryRevision: String? = nil,
annotations: AnnotationsSummary? = nil,
resource: String,
createdAt: String,
@@ -2199,6 +2232,8 @@ public struct SessionSummary: Codable, Sendable {
self.origin = origin
self.project = project
self.workingDirectories = workingDirectories
+ self.repositorySource = repositorySource
+ self.repositoryRevision = repositoryRevision
self.annotations = annotations
self.resource = resource
self.createdAt = createdAt
diff --git a/clients/typescript/test/client.test.ts b/clients/typescript/test/client.test.ts
index 6177c092..56fe0c21 100644
--- a/clients/typescript/test/client.test.ts
+++ b/clients/typescript/test/client.test.ts
@@ -42,6 +42,12 @@ import type {
import { JsonRpcErrorCodes } from '../src/types/common/errors.js';
import { AutomationOperation, type AutomationEntry } from '../src/types/channels-automation/state.js';
import { MessageKind } from '../src/types/channels-chat/state.js';
+import {
+ SessionLifecycle,
+ SessionStatus,
+ type SessionConfigSchema,
+ type SessionState,
+} from '../src/types/index.js';
const ROOT = 'ahp-root://' as const;
const AUTOMATIONS = 'ahp-automations://' as const;
@@ -108,6 +114,150 @@ test('initialize round-trip', async () => {
await client.shutdown();
});
+for (const withRevision of [false, true]) {
+ test(`typed requests round-trip repository intent ${withRevision ? 'with' : 'without'} a revision outside config`, async t => {
+ const [c, s] = InMemoryTransport.pair();
+ const client = new AhpClient(c);
+ t.after(() => client.shutdown());
+ client.connect();
+
+ const schema: SessionConfigSchema = {
+ type: 'object',
+ properties: {
+ mode: { type: 'string', title: 'Mode', default: 'review' },
+ },
+ };
+ const discovery = client.request('resolveSessionConfig', { channel: ROOT });
+ const discoveryRequest = await readRequest(s);
+ assert.equal(discoveryRequest.method, 'resolveSessionConfig');
+ assert.deepEqual(discoveryRequest.params, { channel: ROOT });
+ reply(s, discoveryRequest.id, { schema, values: { mode: 'review' } });
+
+ const discovered = await discovery;
+ assert.deepEqual(discovered.schema, schema);
+ const repository = {
+ repositorySource: 'https://example.org/team/project.git',
+ ...(withRevision ? { repositoryRevision: 'refs/tags/v1.2.3' } : {}),
+ };
+ const config = discovered.values;
+ const resolution = client.request('resolveSessionConfig', { channel: ROOT, ...repository, config });
+ const resolveRequest = await readRequest(s);
+ assert.equal(resolveRequest.method, 'resolveSessionConfig');
+ assert.deepEqual(resolveRequest.params, { channel: ROOT, ...repository, config });
+ reply(s, resolveRequest.id, { schema, values: config });
+ const resolved = await resolution;
+ assert.deepEqual(resolved.values, config);
+
+ const completions = client.request('sessionConfigCompletions', { channel: ROOT, ...repository, config: resolved.values, property: 'mode' });
+ const completionRequest = await readRequest(s);
+ assert.deepEqual(completionRequest.params, { channel: ROOT, ...repository, config: resolved.values, property: 'mode' });
+ reply(s, completionRequest.id, { items: [] });
+ await completions;
+
+ const params = { channel: 'ahp-session:/repository-test', ...repository, config: resolved.values };
+ const creation = client.request('createSession', params);
+ const createRequest = await readRequest(s);
+ assert.equal(createRequest.method, 'createSession');
+ assert.deepEqual(createRequest.params, params);
+ reply(s, createRequest.id, null);
+ assert.equal(await creation, null);
+ });
+}
+
+for (const method of ['resolveSessionConfig', 'sessionConfigCompletions', 'createSession'] as const) {
+ test(`${method} surfaces host rejection of repository intent`, async t => {
+ const [c, s] = InMemoryTransport.pair();
+ const client = new AhpClient(c);
+ t.after(() => client.shutdown());
+ client.connect();
+
+ const channel = method === 'createSession' ? 'ahp-session:/repository-test' : ROOT;
+ const params = {
+ channel,
+ repositorySource: 'https://example.org/team/project.git',
+ repositoryRevision: 'unsupported',
+ ...(method === 'sessionConfigCompletions' ? { property: 'mode' } : {}),
+ };
+ const request = method === 'sessionConfigCompletions'
+ ? client.request(method, { ...params, property: 'mode' })
+ : client.request(method, params);
+ const rejected = assert.rejects(request, new RpcError(JsonRpcErrorCodes.InvalidParams, 'Unsupported repository revision'));
+ const sent = await readRequest(s);
+ assert.deepEqual({ method: sent.method, params: sent.params }, { method, params });
+ replyError(s, sent.id, JsonRpcErrorCodes.InvalidParams, 'Unsupported repository revision');
+ await rejected;
+ });
+}
+
+for (const failed of [false, true]) {
+ test(`session state recovers repository intent and directories after creation ${failed ? 'fails' : 'succeeds'}`, () => {
+ const resource = 'ahp-session:/repository-test';
+ const initial: SessionState = {
+ provider: 'example',
+ title: 'Repository session',
+ status: SessionStatus.Idle,
+ lifecycle: SessionLifecycle.Creating,
+ activeClients: [],
+ chats: [],
+ workingDirectories: [],
+ repositorySource: 'https://example.org/team/project.git',
+ repositoryRevision: 'main',
+ config: {
+ schema: { type: 'object', properties: { mode: { type: 'string', title: 'Mode' } } },
+ values: { mode: 'review' },
+ },
+ };
+ const mirror = new AhpStateMirror();
+ mirror.applySnapshot({ resource, state: initial, fromSeq: 0 });
+ mirror.apply({
+ channel: resource,
+ serverSeq: 1,
+ origin: undefined,
+ action: { type: ActionType.SessionWorkingDirectorySet, directory: 'file:///work/project' },
+ });
+ mirror.apply({
+ channel: resource,
+ serverSeq: 2,
+ origin: undefined,
+ action: { type: ActionType.SessionWorkingDirectorySet, directory: 'file:///work/project-worktree' },
+ });
+ const preparing = mirror.getSession(resource);
+ assert.ok(preparing);
+ assert.equal(preparing.lifecycle, SessionLifecycle.Creating);
+ assert.deepEqual(preparing.config, initial.config);
+ assert.deepEqual([preparing.repositorySource, preparing.repositoryRevision], [initial.repositorySource, initial.repositoryRevision]);
+
+ const joining = new AhpStateMirror();
+ joining.applySnapshot({ resource, state: preparing, fromSeq: 2 });
+ assert.deepEqual(joining.getSession(resource), preparing);
+
+ const completion: ActionEnvelope = {
+ channel: resource,
+ serverSeq: 3,
+ origin: undefined,
+ action: failed
+ ? { type: ActionType.SessionCreationFailed, error: { errorType: 'preparationFailed', message: 'Preparation failed' } }
+ : { type: ActionType.SessionReady },
+ };
+ mirror.apply(completion);
+ joining.apply(completion);
+ const completed = mirror.getSession(resource);
+ assert.ok(completed);
+ assert.equal(completed.lifecycle, failed ? SessionLifecycle.Failed : SessionLifecycle.Ready);
+ assert.deepEqual(completed.config, initial.config);
+ assert.deepEqual([completed.repositorySource, completed.repositoryRevision], [initial.repositorySource, initial.repositoryRevision]);
+ assert.deepEqual(completed.workingDirectories, ['file:///work/project', 'file:///work/project-worktree']);
+ assert.deepEqual(joining.getSession(resource), completed);
+ if (failed) {
+ assert.deepEqual(completed.creationError, { errorType: 'preparationFailed', message: 'Preparation failed' });
+ }
+
+ const reconnected = new AhpStateMirror();
+ reconnected.applySnapshot({ resource, state: completed, fromSeq: 3 });
+ assert.deepEqual(reconnected.getSession(resource), completed);
+ });
+}
+
test('subscribe attaches before sending the request and fans out an action', async () => {
const [c, s] = InMemoryTransport.pair();
const client = new AhpClient(c);
diff --git a/docs/.changes/20260915-repository-session-config.json b/docs/.changes/20260915-repository-session-config.json
new file mode 100644
index 00000000..864da15d
--- /dev/null
+++ b/docs/.changes/20260915-repository-session-config.json
@@ -0,0 +1,4 @@
+{
+ "type": "added",
+ "message": "Typed optional `repositorySource` and `repositoryRevision` request and session metadata fields, with an explicit agent capability for host-owned repository preparation."
+}
diff --git a/docs/specification/root-channel.md b/docs/specification/root-channel.md
index 2ccdad60..c0c71728 100644
--- a/docs/specification/root-channel.md
+++ b/docs/specification/root-channel.md
@@ -185,6 +185,8 @@ The server MAY emit `root/progress` to report incremental progress on a long-run
`progress` is monotonically non-decreasing for a given `progressToken`. `total` is present only when the magnitude is known up front (e.g. a `Content-Length`); when absent, clients SHOULD show an indeterminate indicator. The operation is complete when `progress === total` — the server MUST emit a final frame satisfying this, setting `total` to the final `progress` when the total was never known, after which no further frames reference the token. An optional `message` carries a human-readable description of the work in progress; a client that tracks the token renders its own (localized) label and MAY ignore it, while a generic client MAY display `message` verbatim. The server MAY emit no progress at all (for example when the work was already done), in which case the client simply never shows an indicator. Like the catalogue events, `root/progress` is ephemeral and is **not** replayed on reconnect.
+Completing reported work does not establish session readiness. For [repository-backed creation](./session-channel#repository-backed-creation), hosts MAY use this same progress notification, but clients recover the requested intent, resolved directories, and `creating` / `ready` / `failed` outcome from session state. A minimal client can ignore progress entirely.
+
## Authentication Events
The server MAY emit [`auth/required`](/specification/authentication#auth-expiry-notification) on the root channel when an agent's protected resource needs (re-)authentication. See [Authentication](/specification/authentication) for the full flow.
diff --git a/docs/specification/session-channel.md b/docs/specification/session-channel.md
index 4066ad14..9ca534ba 100644
--- a/docs/specification/session-channel.md
+++ b/docs/specification/session-channel.md
@@ -35,6 +35,81 @@ Subscribers receive a [`SessionState`](/reference/session#sessionstate) snapshot
[`createSession`](/reference/session#createsession) is a JSON-RPC request. The client picks the URI; the server allocates session state and begins backend initialisation. If the URI is already in use the server returns `SessionAlreadyExists` (`-32003`).
+#### Repository-backed creation
+
+A host can offer to prepare **one repository for a new session** through typed session-creation inputs. The client collects repository intent; the host owns authorization, credentials, preparation, and cleanup. This capability does not define reusable projects, a repository catalogue, or a general-purpose clone command.
+
+##### Capability and field constraints
+
+The agent opts in through [`AgentCapabilities.repositorySource`](/reference/root#agentcapabilities). As with other agent capabilities, absence means unsupported and `{}` advertises source-based creation. `{ "revision": true }` additionally supports an explicit revision.
+
+| Request field | Meaning |
+|---|---|
+| `repositorySource` | Credential-free repository URI string identifying the requested source. |
+| `repositoryRevision` | Optional branch, tag, or commit string. |
+
+Both fields are optional typed properties of [`CreateSessionParams`](/reference/session#createsessionparams), [`ResolveSessionConfigParams`](/reference/root#resolvesessionconfigparams), and [`SessionConfigCompletionsParams`](/reference/root#sessionconfigcompletionsparams). The query fields provide context for provider-specific configuration; they are not entries in `config`.
+
+Clients MUST check the capability rather than infer support from a provider name, protocol version, `_meta`, or configuration property. A host MUST NOT accept source input without the capability, or an explicit revision unless `revision` is `true`. Supplying either input in `config` is invalid; hosts MUST reject it rather than silently choose directory/default behavior. There are no alternative standard keys or field-name descriptors.
+
+Advertising support does not make either value required. A request without repository intent retains its existing directory/default behavior. The generated request types and schemas declare the fields and their types; the host enforces capability, authorization, and cross-field constraints. Provider-specific `config` and its schema remain independent.
+
+##### Values and validation
+
+The client supplies the same typed source and optional revision when resolving configuration, requesting configuration completions, and creating the session. Discovery and iterative configuration queries MUST NOT clone or prepare a repository.
+
+For example, the root's agent entry can advertise:
+
+```json
+{
+ "capabilities": {
+ "repositorySource": { "revision": true }
+ }
+}
+```
+
+The client can resolve configuration with `repositorySource` and `repositoryRevision` beside `config`, without a `workingDirectory`, then pass the returned provider configuration to creation:
+
+```json
+{
+ "jsonrpc": "2.0",
+ "id": 2,
+ "method": "createSession",
+ "params": {
+ "channel": "ahp-session:/new-session",
+ "repositorySource": "https://example.org/team/project.git",
+ "repositoryRevision": "main",
+ "config": { "mode": "interactive" }
+ }
+}
+```
+
+The repository URI identifies the source, not a checkout or host filesystem directory. One source can produce multiple directories, including separate checkouts or worktrees; clients MUST NOT use the source URI as a directory identity. `createSession.repositorySource` and a non-empty `createSession.workingDirectories` list are mutually exclusive. Configuration queries may also include an existing `workingDirectory` as context; they do not perform preparation.
+
+When supplied, each value MUST be a non-empty string. A revision without a source is invalid. Omit an unused source or revision instead of supplying an empty string. For creation and configuration queries, the host MUST reject invalid or unsupported intent with `InvalidParams` (`-32602`), including an unsupported source or revision, a malformed or credential-bearing source URI, or conflicting creation directories. It MUST NOT silently drop explicit input, select a default directory, or replace an unsupported revision with its default. A repository-aware client MUST surface invalid capability declarations or unsupported input instead of silently dropping the user's intent.
+
+Repository URIs and configuration values MUST NOT contain credentials such as passwords or access tokens. Authentication uses the existing [authentication contract](./authentication); the host MUST authorize the requesting client before repository side effects and use only credentials permitted for that request. Credentials MUST NOT appear in session state, progress messages, or logs.
+
+##### Preparation and recoverable state
+
+Repository preparation is part of the existing `creating` lifecycle. The host MUST finish preparation before executing turns or publishing `session/ready`. No additional lifecycle state is introduced.
+
+The host MUST publish the accepted, requested source and optional revision as `SessionState.repositorySource` and `SessionState.repositoryRevision` from the initial `creating` snapshot and preserve them through `ready` or `failed`. These immutable fields belong to [`SessionMetadata`](/reference/session#sessionmetadata), so summaries carry the same intent. Preserve requested intent even if the host resolves a branch or tag to a commit; the resolved working location is a separate fact. No configuration action changes these fields.
+
+Before dispatching `session/ready` or `session/creationFailed`, the host MUST publish the actual resolved `workingDirectories` in session state, using the existing snapshot and working-directory actions. While no directory has been resolved, `workingDirectories` MAY be absent or empty; do not claim a checkout was prepared when preparation failed. On failure, the existing `session/creationFailed` action records `lifecycle: "failed"` and `creationError`. Both outcomes retain the requested intent and any resolved directories so clients can recover them from a snapshot or replay.
+
+The host MAY report preparation through the existing `createSession.progressToken` and [`root/progress`](./root-channel#progress). Progress is optional, ephemeral, and not replayed. Neither a completed progress indicator nor a successful command response is a replacement for session readiness or failure state.
+
+##### Reattachment, retry, and cleanup
+
+`createSession` is not an idempotent preparation command. A duplicate URI still returns `SessionAlreadyExists` (`-32003`), including while preparation is running or after creation has failed; it MUST NOT start another preparation for that session. After a lost response, the client should reattach to the same session URI through subscription or [reconnection](./lifecycle#reconnection) and inspect its state. Before treating the recovered session as the requested creation, it MUST verify that its typed `repositorySource` and `repositoryRevision` match the requested intent and inspect the lifecycle. A mismatch is a conflict, not successful recovery. It MUST NOT treat a duplicate creation error as successful recovery. After a failure is addressed, a user can explicitly retry with a new session URI rather than overwrite the failed session.
+
+Cancelling a local wait, disconnecting, or unsubscribing does not grant permission to delete repository data. When the user intends to dispose the session, use the existing `disposeSession` command; this capability adds no cancellation RPC. The host MUST NOT erase a shared checkout or uncommitted user changes during cancellation or disposal. Cleanup of exclusively owned temporary preparation resources remains a host responsibility.
+
+##### Minimal-client behavior
+
+A client supporting this capability collects the source and optional revision separately from provider configuration and sends them as typed request fields. It needs no Git implementation, clone RPC, or progress implementation. Minimal clients can omit the optional capability and continue using directory/default creation. Joining or reconnecting clients read the source, revision, lifecycle and working directories from authoritative session state without repeating preparation.
+
### Active session
Once a session reaches `lifecycle: 'ready'`, clients may create chats on it with [`createChat`](/reference/chat#createchat). Each chat is independently subscribable at its own `ahp-chat:/` URI; see the [Chat Channel specification](./chat-channel) for the per-chat lifecycle, turn flow, tool calls, and input request handling.
diff --git a/schema/actions.schema.json b/schema/actions.schema.json
index 70f7a2b5..33cba59b 100644
--- a/schema/actions.schema.json
+++ b/schema/actions.schema.json
@@ -2943,6 +2943,10 @@
"type": "object",
"description": "Static capabilities an {@link AgentInfo} advertises. Modelled after MCP\ncapabilities: each field is opt-in and its presence (an empty object `{}`)\nsignals support, while absence means the feature is unsupported and the\ncorresponding client commands MUST NOT be used. Sub-fields carry\nper-capability options.",
"properties": {
+ "repositorySource": {
+ "$ref": "#/$defs/RepositorySourceCapability",
+ "description": "The host accepts typed repository inputs for session creation and configuration queries."
+ },
"multipleChats": {
"$ref": "#/$defs/MultipleChatsCapability",
"description": "The agent can host more than one concurrent chat per session. When absent,\nclients MUST NOT call `createChat` to open chats beyond the default one the\nsession starts with. An empty object `{}` advertises multi-chat without\nsource-based creation; set {@link MultipleChatsCapability.fork} or\n{@link MultipleChatsCapability.sideChat} to allow the corresponding mode."
@@ -2953,6 +2957,16 @@
}
}
},
+ "RepositorySourceCapability": {
+ "type": "object",
+ "description": "Options for repository-backed session creation.",
+ "properties": {
+ "revision": {
+ "type": "boolean",
+ "description": "When true, clients may supply an explicit repositoryRevision."
+ }
+ }
+ },
"MultipleChatsCapability": {
"type": "object",
"description": "Options for the {@link AgentCapabilities.multipleChats} capability.",
@@ -3128,6 +3142,14 @@
},
"description": "The working directories the session's agent has tool access to, as\nmaintained by working-directory actions. Directories are equal peers except\nwhen the agent advertises\n{@link MultipleWorkingDirectoriesCapability.immutablePrimary} without\n{@link MultipleWorkingDirectoriesCapability.primaryReplacement} (the first\nentry is then a fixed process root), or advertises `primaryReplacement`\n(the first entry is a protected, replaceable primary slot). Individual chats\nMAY restrict to a subset via\n{@link ChatSummary.workingDirectories | their own `workingDirectories`}; a\nchat that sets none operates against this full set."
},
+ "repositorySource": {
+ "$ref": "#/$defs/URI",
+ "description": "Immutable requested source, separate from the host-resolved working directories."
+ },
+ "repositoryRevision": {
+ "type": "string",
+ "description": "Immutable requested revision, not the checkout's current HEAD."
+ },
"annotations": {
"$ref": "#/$defs/AnnotationsSummary",
"description": "Lightweight summary of this session's inline annotations channel\n(`ahp-session://annotations`). Surfaced so badge UI can render\nannotation / entry counts without subscribing. Absent when the session\ndoes not expose an annotations channel."
@@ -3174,6 +3196,14 @@
},
"description": "The working directories the session's agent has tool access to, as\nmaintained by working-directory actions. Directories are equal peers except\nwhen the agent advertises\n{@link MultipleWorkingDirectoriesCapability.immutablePrimary} without\n{@link MultipleWorkingDirectoriesCapability.primaryReplacement} (the first\nentry is then a fixed process root), or advertises `primaryReplacement`\n(the first entry is a protected, replaceable primary slot). Individual chats\nMAY restrict to a subset via\n{@link ChatSummary.workingDirectories | their own `workingDirectories`}; a\nchat that sets none operates against this full set."
},
+ "repositorySource": {
+ "$ref": "#/$defs/URI",
+ "description": "Immutable requested source, separate from the host-resolved working directories."
+ },
+ "repositoryRevision": {
+ "type": "string",
+ "description": "Immutable requested revision, not the checkout's current HEAD."
+ },
"annotations": {
"$ref": "#/$defs/AnnotationsSummary",
"description": "Lightweight summary of this session's inline annotations channel\n(`ahp-session://annotations`). Surfaced so badge UI can render\nannotation / entry counts without subscribing. Absent when the session\ndoes not expose an annotations channel."
@@ -3213,7 +3243,7 @@
},
"config": {
"$ref": "#/$defs/SessionConfigState",
- "description": "Session configuration schema and current values"
+ "description": "Provider-specific session configuration schema and current values."
},
"customizations": {
"type": "array",
@@ -3482,6 +3512,14 @@
},
"description": "The working directories the session's agent has tool access to, as\nmaintained by working-directory actions. Directories are equal peers except\nwhen the agent advertises\n{@link MultipleWorkingDirectoriesCapability.immutablePrimary} without\n{@link MultipleWorkingDirectoriesCapability.primaryReplacement} (the first\nentry is then a fixed process root), or advertises `primaryReplacement`\n(the first entry is a protected, replaceable primary slot). Individual chats\nMAY restrict to a subset via\n{@link ChatSummary.workingDirectories | their own `workingDirectories`}; a\nchat that sets none operates against this full set."
},
+ "repositorySource": {
+ "$ref": "#/$defs/URI",
+ "description": "Immutable requested source, separate from the host-resolved working directories."
+ },
+ "repositoryRevision": {
+ "type": "string",
+ "description": "Immutable requested revision, not the checkout's current HEAD."
+ },
"annotations": {
"$ref": "#/$defs/AnnotationsSummary",
"description": "Lightweight summary of this session's inline annotations channel\n(`ahp-session://annotations`). Surfaced so badge UI can render\nannotation / entry counts without subscribing. Absent when the session\ndoes not expose an annotations channel."
diff --git a/schema/commands.schema.json b/schema/commands.schema.json
index 22902368..f9a3deca 100644
--- a/schema/commands.schema.json
+++ b/schema/commands.schema.json
@@ -949,7 +949,7 @@
},
"ResolveSessionConfigParams": {
"type": "object",
- "description": "Iteratively resolves the session configuration schema. The client sends the\ncurrent partial session config and any user-filled metadata values. The server\nreturns a property schema describing what additional metadata is needed,\ncontextual to the current selections.\n\nThe client calls this command whenever the user changes a significant input\n(e.g. picks a working directory, toggles a property). Each response returns\nthe full current property set (not a delta). The returned `values` contain\nserver-resolved defaults to pass to `createSession`.",
+ "description": "Iteratively resolves the session configuration schema. The client sends the\ncurrent partial session config and any user-filled metadata values. The server\nreturns a property schema describing what additional metadata is needed,\ncontextual to the current selections.\n\nThe client calls this command whenever the user changes a significant input\n(e.g. picks a working directory, toggles a property). Each response returns\nthe full current property set (not a delta). The returned `values` contain\nserver-resolved defaults to pass to `createSession`.\n\nThis command MUST NOT clone or prepare a repository. Repository context\nrequires the agent's `repositorySource` capability.",
"properties": {
"channel": {
"type": "string",
@@ -970,10 +970,18 @@
"$ref": "#/$defs/URI",
"description": "Working directory for the session"
},
+ "repositorySource": {
+ "$ref": "#/$defs/URI",
+ "description": "Credential-free source context; not a working-directory URI."
+ },
+ "repositoryRevision": {
+ "type": "string",
+ "description": "Requested revision; requires a source and the capability's revision option."
+ },
"config": {
"type": "object",
"additionalProperties": {},
- "description": "Current user-filled configuration values"
+ "description": "Current user-filled configuration values; see {@link SessionConfigSchema}."
}
},
"required": [
@@ -1044,6 +1052,14 @@
"$ref": "#/$defs/URI",
"description": "Working directory for the session"
},
+ "repositorySource": {
+ "$ref": "#/$defs/URI",
+ "description": "Repository context for configuration completions; this MUST NOT prepare a checkout."
+ },
+ "repositoryRevision": {
+ "type": "string",
+ "description": "Requested revision; requires a source and the capability's revision option."
+ },
"config": {
"type": "object",
"additionalProperties": {},
@@ -1081,7 +1097,7 @@
},
"CreateSessionParams": {
"type": "object",
- "description": "Creates a new session with the specified agent provider.\n\nIf the session URI already exists, the server MUST return an error with code\n`-32003` (`SessionAlreadyExists`).\n\nAfter creation, the client should subscribe to the session URI to receive state\nupdates. The server also broadcasts a `root/sessionAdded` notification to all\nclients.",
+ "description": "Creates a new session with the specified agent provider.\n\nIf the session URI already exists, the server MUST return an error with code\n`-32003` (`SessionAlreadyExists`).\n\nAfter creation, the client should subscribe to the session URI to receive state\nupdates. The server also broadcasts a `root/sessionAdded` notification to all\nclients.\n\nRepository preparation MUST finish before `session/ready` or executing turns.\nClients recover the outcome from session state, not progress notifications.",
"properties": {
"channel": {
"$ref": "#/$defs/URI",
@@ -1101,12 +1117,20 @@
"items": {
"$ref": "#/$defs/URI"
},
- "description": "The working directories the session's agent is granted tool access to.\nA session may span multiple directories; they are equal peers except when\nthe agent advertises a protected-primary capability. An\n{@link MultipleWorkingDirectoriesCapability.immutablePrimary | immutable\nprimary} is fixed, while a\n{@link MultipleWorkingDirectoriesCapability.primaryReplacement | replaceable\nprimary} is changed only with `session/workingDirectoryReplaced`.\n\nA client MUST NOT supply more than one entry unless the agent advertises\n{@link AgentCapabilities.multipleWorkingDirectories}; a server without that\ncapability treats only the first entry as the session's working directory\nand ignores the rest. Dispatch working-directory actions to change the set\nafter the session has started."
+ "description": "The working directories the session's agent is granted tool access to.\nA session may span multiple directories; they are equal peers except when\nthe agent advertises a protected-primary capability. An\n{@link MultipleWorkingDirectoriesCapability.immutablePrimary | immutable\nprimary} is fixed, while a\n{@link MultipleWorkingDirectoriesCapability.primaryReplacement | replaceable\nprimary} is changed only with `session/workingDirectoryReplaced`.\n\nA client MUST NOT supply more than one entry unless the agent advertises\n{@link AgentCapabilities.multipleWorkingDirectories}; a server without that\ncapability treats only the first entry as the session's working directory\nand ignores the rest. Dispatch working-directory actions to change the set\nafter the session has started.\n\nA non-empty list and `repositorySource` are mutually exclusive.\nA repository URI identifies the source, not a working-directory URI; one\nsource may produce multiple directories."
+ },
+ "repositorySource": {
+ "$ref": "#/$defs/URI",
+ "description": "Credential-free source to prepare; requires the agent's repositorySource capability."
+ },
+ "repositoryRevision": {
+ "type": "string",
+ "description": "Requested branch, tag, or commit; requires a source and the capability's revision option."
},
"config": {
"type": "object",
"additionalProperties": {},
- "description": "Agent-specific configuration values collected via `resolveSessionConfig`.\nKeys and values correspond to the schema returned by the server."
+ "description": "Session configuration values collected via `resolveSessionConfig`.\nKeys and values follow the advertised {@link SessionConfigSchema}."
},
"activeClient": {
"$ref": "#/$defs/SessionActiveClient",
@@ -1123,7 +1147,7 @@
},
"DisposeSessionParams": {
"type": "object",
- "description": "Disposes a session and cleans up server-side resources.\n\nThe server broadcasts a `root/sessionRemoved` notification to all clients.",
+ "description": "Disposes a session and cleans up server-side resources.\n\nThe server broadcasts a `root/sessionRemoved` notification to all clients.\nDisposal MUST NOT erase a shared checkout or uncommitted user changes.\nRepository cleanup remains host-owned; ending a client's wait or subscription\ndoes not grant permission to delete repository data.",
"properties": {
"channel": {
"$ref": "#/$defs/URI",
@@ -2187,6 +2211,10 @@
"type": "object",
"description": "Static capabilities an {@link AgentInfo} advertises. Modelled after MCP\ncapabilities: each field is opt-in and its presence (an empty object `{}`)\nsignals support, while absence means the feature is unsupported and the\ncorresponding client commands MUST NOT be used. Sub-fields carry\nper-capability options.",
"properties": {
+ "repositorySource": {
+ "$ref": "#/$defs/RepositorySourceCapability",
+ "description": "The host accepts typed repository inputs for session creation and configuration queries."
+ },
"multipleChats": {
"$ref": "#/$defs/MultipleChatsCapability",
"description": "The agent can host more than one concurrent chat per session. When absent,\nclients MUST NOT call `createChat` to open chats beyond the default one the\nsession starts with. An empty object `{}` advertises multi-chat without\nsource-based creation; set {@link MultipleChatsCapability.fork} or\n{@link MultipleChatsCapability.sideChat} to allow the corresponding mode."
@@ -2197,6 +2225,16 @@
}
}
},
+ "RepositorySourceCapability": {
+ "type": "object",
+ "description": "Options for repository-backed session creation.",
+ "properties": {
+ "revision": {
+ "type": "boolean",
+ "description": "When true, clients may supply an explicit repositoryRevision."
+ }
+ }
+ },
"MultipleChatsCapability": {
"type": "object",
"description": "Options for the {@link AgentCapabilities.multipleChats} capability.",
@@ -2372,6 +2410,14 @@
},
"description": "The working directories the session's agent has tool access to, as\nmaintained by working-directory actions. Directories are equal peers except\nwhen the agent advertises\n{@link MultipleWorkingDirectoriesCapability.immutablePrimary} without\n{@link MultipleWorkingDirectoriesCapability.primaryReplacement} (the first\nentry is then a fixed process root), or advertises `primaryReplacement`\n(the first entry is a protected, replaceable primary slot). Individual chats\nMAY restrict to a subset via\n{@link ChatSummary.workingDirectories | their own `workingDirectories`}; a\nchat that sets none operates against this full set."
},
+ "repositorySource": {
+ "$ref": "#/$defs/URI",
+ "description": "Immutable requested source, separate from the host-resolved working directories."
+ },
+ "repositoryRevision": {
+ "type": "string",
+ "description": "Immutable requested revision, not the checkout's current HEAD."
+ },
"annotations": {
"$ref": "#/$defs/AnnotationsSummary",
"description": "Lightweight summary of this session's inline annotations channel\n(`ahp-session://annotations`). Surfaced so badge UI can render\nannotation / entry counts without subscribing. Absent when the session\ndoes not expose an annotations channel."
@@ -2418,6 +2464,14 @@
},
"description": "The working directories the session's agent has tool access to, as\nmaintained by working-directory actions. Directories are equal peers except\nwhen the agent advertises\n{@link MultipleWorkingDirectoriesCapability.immutablePrimary} without\n{@link MultipleWorkingDirectoriesCapability.primaryReplacement} (the first\nentry is then a fixed process root), or advertises `primaryReplacement`\n(the first entry is a protected, replaceable primary slot). Individual chats\nMAY restrict to a subset via\n{@link ChatSummary.workingDirectories | their own `workingDirectories`}; a\nchat that sets none operates against this full set."
},
+ "repositorySource": {
+ "$ref": "#/$defs/URI",
+ "description": "Immutable requested source, separate from the host-resolved working directories."
+ },
+ "repositoryRevision": {
+ "type": "string",
+ "description": "Immutable requested revision, not the checkout's current HEAD."
+ },
"annotations": {
"$ref": "#/$defs/AnnotationsSummary",
"description": "Lightweight summary of this session's inline annotations channel\n(`ahp-session://annotations`). Surfaced so badge UI can render\nannotation / entry counts without subscribing. Absent when the session\ndoes not expose an annotations channel."
@@ -2457,7 +2511,7 @@
},
"config": {
"$ref": "#/$defs/SessionConfigState",
- "description": "Session configuration schema and current values"
+ "description": "Provider-specific session configuration schema and current values."
},
"customizations": {
"type": "array",
@@ -2726,6 +2780,14 @@
},
"description": "The working directories the session's agent has tool access to, as\nmaintained by working-directory actions. Directories are equal peers except\nwhen the agent advertises\n{@link MultipleWorkingDirectoriesCapability.immutablePrimary} without\n{@link MultipleWorkingDirectoriesCapability.primaryReplacement} (the first\nentry is then a fixed process root), or advertises `primaryReplacement`\n(the first entry is a protected, replaceable primary slot). Individual chats\nMAY restrict to a subset via\n{@link ChatSummary.workingDirectories | their own `workingDirectories`}; a\nchat that sets none operates against this full set."
},
+ "repositorySource": {
+ "$ref": "#/$defs/URI",
+ "description": "Immutable requested source, separate from the host-resolved working directories."
+ },
+ "repositoryRevision": {
+ "type": "string",
+ "description": "Immutable requested revision, not the checkout's current HEAD."
+ },
"annotations": {
"$ref": "#/$defs/AnnotationsSummary",
"description": "Lightweight summary of this session's inline annotations channel\n(`ahp-session://annotations`). Surfaced so badge UI can render\nannotation / entry counts without subscribing. Absent when the session\ndoes not expose an annotations channel."
diff --git a/schema/errors.schema.json b/schema/errors.schema.json
index 71cb746b..74175b8a 100644
--- a/schema/errors.schema.json
+++ b/schema/errors.schema.json
@@ -616,6 +616,10 @@
"type": "object",
"description": "Static capabilities an {@link AgentInfo} advertises. Modelled after MCP\ncapabilities: each field is opt-in and its presence (an empty object `{}`)\nsignals support, while absence means the feature is unsupported and the\ncorresponding client commands MUST NOT be used. Sub-fields carry\nper-capability options.",
"properties": {
+ "repositorySource": {
+ "$ref": "#/$defs/RepositorySourceCapability",
+ "description": "The host accepts typed repository inputs for session creation and configuration queries."
+ },
"multipleChats": {
"$ref": "#/$defs/MultipleChatsCapability",
"description": "The agent can host more than one concurrent chat per session. When absent,\nclients MUST NOT call `createChat` to open chats beyond the default one the\nsession starts with. An empty object `{}` advertises multi-chat without\nsource-based creation; set {@link MultipleChatsCapability.fork} or\n{@link MultipleChatsCapability.sideChat} to allow the corresponding mode."
@@ -626,6 +630,16 @@
}
}
},
+ "RepositorySourceCapability": {
+ "type": "object",
+ "description": "Options for repository-backed session creation.",
+ "properties": {
+ "revision": {
+ "type": "boolean",
+ "description": "When true, clients may supply an explicit repositoryRevision."
+ }
+ }
+ },
"MultipleChatsCapability": {
"type": "object",
"description": "Options for the {@link AgentCapabilities.multipleChats} capability.",
@@ -801,6 +815,14 @@
},
"description": "The working directories the session's agent has tool access to, as\nmaintained by working-directory actions. Directories are equal peers except\nwhen the agent advertises\n{@link MultipleWorkingDirectoriesCapability.immutablePrimary} without\n{@link MultipleWorkingDirectoriesCapability.primaryReplacement} (the first\nentry is then a fixed process root), or advertises `primaryReplacement`\n(the first entry is a protected, replaceable primary slot). Individual chats\nMAY restrict to a subset via\n{@link ChatSummary.workingDirectories | their own `workingDirectories`}; a\nchat that sets none operates against this full set."
},
+ "repositorySource": {
+ "$ref": "#/$defs/URI",
+ "description": "Immutable requested source, separate from the host-resolved working directories."
+ },
+ "repositoryRevision": {
+ "type": "string",
+ "description": "Immutable requested revision, not the checkout's current HEAD."
+ },
"annotations": {
"$ref": "#/$defs/AnnotationsSummary",
"description": "Lightweight summary of this session's inline annotations channel\n(`ahp-session://annotations`). Surfaced so badge UI can render\nannotation / entry counts without subscribing. Absent when the session\ndoes not expose an annotations channel."
@@ -847,6 +869,14 @@
},
"description": "The working directories the session's agent has tool access to, as\nmaintained by working-directory actions. Directories are equal peers except\nwhen the agent advertises\n{@link MultipleWorkingDirectoriesCapability.immutablePrimary} without\n{@link MultipleWorkingDirectoriesCapability.primaryReplacement} (the first\nentry is then a fixed process root), or advertises `primaryReplacement`\n(the first entry is a protected, replaceable primary slot). Individual chats\nMAY restrict to a subset via\n{@link ChatSummary.workingDirectories | their own `workingDirectories`}; a\nchat that sets none operates against this full set."
},
+ "repositorySource": {
+ "$ref": "#/$defs/URI",
+ "description": "Immutable requested source, separate from the host-resolved working directories."
+ },
+ "repositoryRevision": {
+ "type": "string",
+ "description": "Immutable requested revision, not the checkout's current HEAD."
+ },
"annotations": {
"$ref": "#/$defs/AnnotationsSummary",
"description": "Lightweight summary of this session's inline annotations channel\n(`ahp-session://annotations`). Surfaced so badge UI can render\nannotation / entry counts without subscribing. Absent when the session\ndoes not expose an annotations channel."
@@ -886,7 +916,7 @@
},
"config": {
"$ref": "#/$defs/SessionConfigState",
- "description": "Session configuration schema and current values"
+ "description": "Provider-specific session configuration schema and current values."
},
"customizations": {
"type": "array",
@@ -1155,6 +1185,14 @@
},
"description": "The working directories the session's agent has tool access to, as\nmaintained by working-directory actions. Directories are equal peers except\nwhen the agent advertises\n{@link MultipleWorkingDirectoriesCapability.immutablePrimary} without\n{@link MultipleWorkingDirectoriesCapability.primaryReplacement} (the first\nentry is then a fixed process root), or advertises `primaryReplacement`\n(the first entry is a protected, replaceable primary slot). Individual chats\nMAY restrict to a subset via\n{@link ChatSummary.workingDirectories | their own `workingDirectories`}; a\nchat that sets none operates against this full set."
},
+ "repositorySource": {
+ "$ref": "#/$defs/URI",
+ "description": "Immutable requested source, separate from the host-resolved working directories."
+ },
+ "repositoryRevision": {
+ "type": "string",
+ "description": "Immutable requested revision, not the checkout's current HEAD."
+ },
"annotations": {
"$ref": "#/$defs/AnnotationsSummary",
"description": "Lightweight summary of this session's inline annotations channel\n(`ahp-session://annotations`). Surfaced so badge UI can render\nannotation / entry counts without subscribing. Absent when the session\ndoes not expose an annotations channel."
@@ -6601,7 +6639,7 @@
},
"ResolveSessionConfigParams": {
"type": "object",
- "description": "Iteratively resolves the session configuration schema. The client sends the\ncurrent partial session config and any user-filled metadata values. The server\nreturns a property schema describing what additional metadata is needed,\ncontextual to the current selections.\n\nThe client calls this command whenever the user changes a significant input\n(e.g. picks a working directory, toggles a property). Each response returns\nthe full current property set (not a delta). The returned `values` contain\nserver-resolved defaults to pass to `createSession`.",
+ "description": "Iteratively resolves the session configuration schema. The client sends the\ncurrent partial session config and any user-filled metadata values. The server\nreturns a property schema describing what additional metadata is needed,\ncontextual to the current selections.\n\nThe client calls this command whenever the user changes a significant input\n(e.g. picks a working directory, toggles a property). Each response returns\nthe full current property set (not a delta). The returned `values` contain\nserver-resolved defaults to pass to `createSession`.\n\nThis command MUST NOT clone or prepare a repository. Repository context\nrequires the agent's `repositorySource` capability.",
"properties": {
"channel": {
"type": "string",
@@ -6622,10 +6660,18 @@
"$ref": "#/$defs/URI",
"description": "Working directory for the session"
},
+ "repositorySource": {
+ "$ref": "#/$defs/URI",
+ "description": "Credential-free source context; not a working-directory URI."
+ },
+ "repositoryRevision": {
+ "type": "string",
+ "description": "Requested revision; requires a source and the capability's revision option."
+ },
"config": {
"type": "object",
"additionalProperties": {},
- "description": "Current user-filled configuration values"
+ "description": "Current user-filled configuration values; see {@link SessionConfigSchema}."
}
},
"required": [
@@ -6696,6 +6742,14 @@
"$ref": "#/$defs/URI",
"description": "Working directory for the session"
},
+ "repositorySource": {
+ "$ref": "#/$defs/URI",
+ "description": "Repository context for configuration completions; this MUST NOT prepare a checkout."
+ },
+ "repositoryRevision": {
+ "type": "string",
+ "description": "Requested revision; requires a source and the capability's revision option."
+ },
"config": {
"type": "object",
"additionalProperties": {},
@@ -6733,7 +6787,7 @@
},
"CreateSessionParams": {
"type": "object",
- "description": "Creates a new session with the specified agent provider.\n\nIf the session URI already exists, the server MUST return an error with code\n`-32003` (`SessionAlreadyExists`).\n\nAfter creation, the client should subscribe to the session URI to receive state\nupdates. The server also broadcasts a `root/sessionAdded` notification to all\nclients.",
+ "description": "Creates a new session with the specified agent provider.\n\nIf the session URI already exists, the server MUST return an error with code\n`-32003` (`SessionAlreadyExists`).\n\nAfter creation, the client should subscribe to the session URI to receive state\nupdates. The server also broadcasts a `root/sessionAdded` notification to all\nclients.\n\nRepository preparation MUST finish before `session/ready` or executing turns.\nClients recover the outcome from session state, not progress notifications.",
"properties": {
"channel": {
"$ref": "#/$defs/URI",
@@ -6753,12 +6807,20 @@
"items": {
"$ref": "#/$defs/URI"
},
- "description": "The working directories the session's agent is granted tool access to.\nA session may span multiple directories; they are equal peers except when\nthe agent advertises a protected-primary capability. An\n{@link MultipleWorkingDirectoriesCapability.immutablePrimary | immutable\nprimary} is fixed, while a\n{@link MultipleWorkingDirectoriesCapability.primaryReplacement | replaceable\nprimary} is changed only with `session/workingDirectoryReplaced`.\n\nA client MUST NOT supply more than one entry unless the agent advertises\n{@link AgentCapabilities.multipleWorkingDirectories}; a server without that\ncapability treats only the first entry as the session's working directory\nand ignores the rest. Dispatch working-directory actions to change the set\nafter the session has started."
+ "description": "The working directories the session's agent is granted tool access to.\nA session may span multiple directories; they are equal peers except when\nthe agent advertises a protected-primary capability. An\n{@link MultipleWorkingDirectoriesCapability.immutablePrimary | immutable\nprimary} is fixed, while a\n{@link MultipleWorkingDirectoriesCapability.primaryReplacement | replaceable\nprimary} is changed only with `session/workingDirectoryReplaced`.\n\nA client MUST NOT supply more than one entry unless the agent advertises\n{@link AgentCapabilities.multipleWorkingDirectories}; a server without that\ncapability treats only the first entry as the session's working directory\nand ignores the rest. Dispatch working-directory actions to change the set\nafter the session has started.\n\nA non-empty list and `repositorySource` are mutually exclusive.\nA repository URI identifies the source, not a working-directory URI; one\nsource may produce multiple directories."
+ },
+ "repositorySource": {
+ "$ref": "#/$defs/URI",
+ "description": "Credential-free source to prepare; requires the agent's repositorySource capability."
+ },
+ "repositoryRevision": {
+ "type": "string",
+ "description": "Requested branch, tag, or commit; requires a source and the capability's revision option."
},
"config": {
"type": "object",
"additionalProperties": {},
- "description": "Agent-specific configuration values collected via `resolveSessionConfig`.\nKeys and values correspond to the schema returned by the server."
+ "description": "Session configuration values collected via `resolveSessionConfig`.\nKeys and values follow the advertised {@link SessionConfigSchema}."
},
"activeClient": {
"$ref": "#/$defs/SessionActiveClient",
@@ -6775,7 +6837,7 @@
},
"DisposeSessionParams": {
"type": "object",
- "description": "Disposes a session and cleans up server-side resources.\n\nThe server broadcasts a `root/sessionRemoved` notification to all clients.",
+ "description": "Disposes a session and cleans up server-side resources.\n\nThe server broadcasts a `root/sessionRemoved` notification to all clients.\nDisposal MUST NOT erase a shared checkout or uncommitted user changes.\nRepository cleanup remains host-owned; ending a client's wait or subscription\ndoes not grant permission to delete repository data.",
"properties": {
"channel": {
"$ref": "#/$defs/URI",
diff --git a/schema/notifications.schema.json b/schema/notifications.schema.json
index 01271335..f7148dc2 100644
--- a/schema/notifications.schema.json
+++ b/schema/notifications.schema.json
@@ -109,6 +109,14 @@
},
"description": "The working directories the session's agent has tool access to, as\nmaintained by working-directory actions. Directories are equal peers except\nwhen the agent advertises\n{@link MultipleWorkingDirectoriesCapability.immutablePrimary} without\n{@link MultipleWorkingDirectoriesCapability.primaryReplacement} (the first\nentry is then a fixed process root), or advertises `primaryReplacement`\n(the first entry is a protected, replaceable primary slot). Individual chats\nMAY restrict to a subset via\n{@link ChatSummary.workingDirectories | their own `workingDirectories`}; a\nchat that sets none operates against this full set."
},
+ "repositorySource": {
+ "$ref": "#/$defs/URI",
+ "description": "Immutable requested source, separate from the host-resolved working directories."
+ },
+ "repositoryRevision": {
+ "type": "string",
+ "description": "Immutable requested revision, not the checkout's current HEAD."
+ },
"annotations": {
"$ref": "#/$defs/AnnotationsSummary",
"description": "Lightweight summary of this session's inline annotations channel\n(`ahp-session://annotations`). Surfaced so badge UI can render\nannotation / entry counts without subscribing. Absent when the session\ndoes not expose an annotations channel."
@@ -146,7 +154,7 @@
},
"ProgressParams": {
"type": "object",
- "description": "Generic progress notification for a long-running operation.\n\nA client opts in to progress for a request by including a `progressToken` in\nthat request (today: the `progressToken` field on `createSession`). If the\nserver does long-running work to service the request — e.g. lazily\ndownloading an agent's native SDK the first time a session of that provider\nis materialized — it emits `progress` notifications carrying the same token.\n\nThe notification is operation-agnostic: it says nothing about *what* is\nprogressing. The client correlates `progressToken` back to the request it\noriginated from (and thus the UI surface awaiting it) and renders its own\nlocalized indicator. The same channel serves any future long-running\noperation without a new method.\n\nSemantics:\n\n- `progress` is monotonically non-decreasing for a given `progressToken`.\n- `total` is present only when the server knows the magnitude up front\n (e.g. a `Content-Length`); when absent the client SHOULD show an\n indeterminate indicator.\n- The operation is complete when `progress === total`. The server MUST emit a\n final frame satisfying `progress === total`; when the total was never\n known, it sets `total` to the final `progress` on that frame. No further\n frames reference the token afterwards.\n- The server MAY emit no progress at all (e.g. the work was already done);\n the client then never shows an indicator.\n- Like all notifications this is ephemeral and is **not** replayed on\n reconnect. A client that never receives the terminal frame SHOULD expire\n the indicator after an idle timeout.",
+ "description": "Generic progress notification for a long-running operation.\n\nA client opts in to progress for a request by including a `progressToken` in\nthat request (today: the `progressToken` field on `createSession`). If the\nserver does long-running work to service the request — e.g. lazily\ndownloading an agent's native SDK the first time a session of that provider\nis materialized — it emits `progress` notifications carrying the same token.\n\nThe notification is operation-agnostic: it says nothing about *what* is\nprogressing. The client correlates `progressToken` back to the request it\noriginated from (and thus the UI surface awaiting it) and renders its own\nlocalized indicator. The same channel serves any future long-running\noperation without a new method.\n\nSemantics:\n\n- `progress` is monotonically non-decreasing for a given `progressToken`.\n- `total` is present only when the server knows the magnitude up front\n (e.g. a `Content-Length`); when absent the client SHOULD show an\n indeterminate indicator.\n- The operation is complete when `progress === total`. The server MUST emit a\n final frame satisfying `progress === total`; when the total was never\n known, it sets `total` to the final `progress` on that frame. No further\n frames reference the token afterwards.\n- The server MAY emit no progress at all (e.g. the work was already done);\n the client then never shows an indicator.\n- Like all notifications this is ephemeral and is **not** replayed on\n reconnect. A client that never receives the terminal frame SHOULD expire\n the indicator after an idle timeout.\n- Completion of reported work does not establish session readiness.\n Observe session lifecycle state for the durable outcome.",
"properties": {
"channel": {
"$ref": "#/$defs/URI",
@@ -783,6 +791,10 @@
"type": "object",
"description": "Static capabilities an {@link AgentInfo} advertises. Modelled after MCP\ncapabilities: each field is opt-in and its presence (an empty object `{}`)\nsignals support, while absence means the feature is unsupported and the\ncorresponding client commands MUST NOT be used. Sub-fields carry\nper-capability options.",
"properties": {
+ "repositorySource": {
+ "$ref": "#/$defs/RepositorySourceCapability",
+ "description": "The host accepts typed repository inputs for session creation and configuration queries."
+ },
"multipleChats": {
"$ref": "#/$defs/MultipleChatsCapability",
"description": "The agent can host more than one concurrent chat per session. When absent,\nclients MUST NOT call `createChat` to open chats beyond the default one the\nsession starts with. An empty object `{}` advertises multi-chat without\nsource-based creation; set {@link MultipleChatsCapability.fork} or\n{@link MultipleChatsCapability.sideChat} to allow the corresponding mode."
@@ -793,6 +805,16 @@
}
}
},
+ "RepositorySourceCapability": {
+ "type": "object",
+ "description": "Options for repository-backed session creation.",
+ "properties": {
+ "revision": {
+ "type": "boolean",
+ "description": "When true, clients may supply an explicit repositoryRevision."
+ }
+ }
+ },
"MultipleChatsCapability": {
"type": "object",
"description": "Options for the {@link AgentCapabilities.multipleChats} capability.",
@@ -968,6 +990,14 @@
},
"description": "The working directories the session's agent has tool access to, as\nmaintained by working-directory actions. Directories are equal peers except\nwhen the agent advertises\n{@link MultipleWorkingDirectoriesCapability.immutablePrimary} without\n{@link MultipleWorkingDirectoriesCapability.primaryReplacement} (the first\nentry is then a fixed process root), or advertises `primaryReplacement`\n(the first entry is a protected, replaceable primary slot). Individual chats\nMAY restrict to a subset via\n{@link ChatSummary.workingDirectories | their own `workingDirectories`}; a\nchat that sets none operates against this full set."
},
+ "repositorySource": {
+ "$ref": "#/$defs/URI",
+ "description": "Immutable requested source, separate from the host-resolved working directories."
+ },
+ "repositoryRevision": {
+ "type": "string",
+ "description": "Immutable requested revision, not the checkout's current HEAD."
+ },
"annotations": {
"$ref": "#/$defs/AnnotationsSummary",
"description": "Lightweight summary of this session's inline annotations channel\n(`ahp-session://annotations`). Surfaced so badge UI can render\nannotation / entry counts without subscribing. Absent when the session\ndoes not expose an annotations channel."
@@ -1014,6 +1044,14 @@
},
"description": "The working directories the session's agent has tool access to, as\nmaintained by working-directory actions. Directories are equal peers except\nwhen the agent advertises\n{@link MultipleWorkingDirectoriesCapability.immutablePrimary} without\n{@link MultipleWorkingDirectoriesCapability.primaryReplacement} (the first\nentry is then a fixed process root), or advertises `primaryReplacement`\n(the first entry is a protected, replaceable primary slot). Individual chats\nMAY restrict to a subset via\n{@link ChatSummary.workingDirectories | their own `workingDirectories`}; a\nchat that sets none operates against this full set."
},
+ "repositorySource": {
+ "$ref": "#/$defs/URI",
+ "description": "Immutable requested source, separate from the host-resolved working directories."
+ },
+ "repositoryRevision": {
+ "type": "string",
+ "description": "Immutable requested revision, not the checkout's current HEAD."
+ },
"annotations": {
"$ref": "#/$defs/AnnotationsSummary",
"description": "Lightweight summary of this session's inline annotations channel\n(`ahp-session://annotations`). Surfaced so badge UI can render\nannotation / entry counts without subscribing. Absent when the session\ndoes not expose an annotations channel."
@@ -1053,7 +1091,7 @@
},
"config": {
"$ref": "#/$defs/SessionConfigState",
- "description": "Session configuration schema and current values"
+ "description": "Provider-specific session configuration schema and current values."
},
"customizations": {
"type": "array",
@@ -1322,6 +1360,14 @@
},
"description": "The working directories the session's agent has tool access to, as\nmaintained by working-directory actions. Directories are equal peers except\nwhen the agent advertises\n{@link MultipleWorkingDirectoriesCapability.immutablePrimary} without\n{@link MultipleWorkingDirectoriesCapability.primaryReplacement} (the first\nentry is then a fixed process root), or advertises `primaryReplacement`\n(the first entry is a protected, replaceable primary slot). Individual chats\nMAY restrict to a subset via\n{@link ChatSummary.workingDirectories | their own `workingDirectories`}; a\nchat that sets none operates against this full set."
},
+ "repositorySource": {
+ "$ref": "#/$defs/URI",
+ "description": "Immutable requested source, separate from the host-resolved working directories."
+ },
+ "repositoryRevision": {
+ "type": "string",
+ "description": "Immutable requested revision, not the checkout's current HEAD."
+ },
"annotations": {
"$ref": "#/$defs/AnnotationsSummary",
"description": "Lightweight summary of this session's inline annotations channel\n(`ahp-session://annotations`). Surfaced so badge UI can render\nannotation / entry counts without subscribing. Absent when the session\ndoes not expose an annotations channel."
diff --git a/schema/state.schema.json b/schema/state.schema.json
index 097236df..3e4a1b6a 100644
--- a/schema/state.schema.json
+++ b/schema/state.schema.json
@@ -527,6 +527,10 @@
"type": "object",
"description": "Static capabilities an {@link AgentInfo} advertises. Modelled after MCP\ncapabilities: each field is opt-in and its presence (an empty object `{}`)\nsignals support, while absence means the feature is unsupported and the\ncorresponding client commands MUST NOT be used. Sub-fields carry\nper-capability options.",
"properties": {
+ "repositorySource": {
+ "$ref": "#/$defs/RepositorySourceCapability",
+ "description": "The host accepts typed repository inputs for session creation and configuration queries."
+ },
"multipleChats": {
"$ref": "#/$defs/MultipleChatsCapability",
"description": "The agent can host more than one concurrent chat per session. When absent,\nclients MUST NOT call `createChat` to open chats beyond the default one the\nsession starts with. An empty object `{}` advertises multi-chat without\nsource-based creation; set {@link MultipleChatsCapability.fork} or\n{@link MultipleChatsCapability.sideChat} to allow the corresponding mode."
@@ -537,6 +541,16 @@
}
}
},
+ "RepositorySourceCapability": {
+ "type": "object",
+ "description": "Options for repository-backed session creation.",
+ "properties": {
+ "revision": {
+ "type": "boolean",
+ "description": "When true, clients may supply an explicit repositoryRevision."
+ }
+ }
+ },
"MultipleChatsCapability": {
"type": "object",
"description": "Options for the {@link AgentCapabilities.multipleChats} capability.",
@@ -712,6 +726,14 @@
},
"description": "The working directories the session's agent has tool access to, as\nmaintained by working-directory actions. Directories are equal peers except\nwhen the agent advertises\n{@link MultipleWorkingDirectoriesCapability.immutablePrimary} without\n{@link MultipleWorkingDirectoriesCapability.primaryReplacement} (the first\nentry is then a fixed process root), or advertises `primaryReplacement`\n(the first entry is a protected, replaceable primary slot). Individual chats\nMAY restrict to a subset via\n{@link ChatSummary.workingDirectories | their own `workingDirectories`}; a\nchat that sets none operates against this full set."
},
+ "repositorySource": {
+ "$ref": "#/$defs/URI",
+ "description": "Immutable requested source, separate from the host-resolved working directories."
+ },
+ "repositoryRevision": {
+ "type": "string",
+ "description": "Immutable requested revision, not the checkout's current HEAD."
+ },
"annotations": {
"$ref": "#/$defs/AnnotationsSummary",
"description": "Lightweight summary of this session's inline annotations channel\n(`ahp-session://annotations`). Surfaced so badge UI can render\nannotation / entry counts without subscribing. Absent when the session\ndoes not expose an annotations channel."
@@ -758,6 +780,14 @@
},
"description": "The working directories the session's agent has tool access to, as\nmaintained by working-directory actions. Directories are equal peers except\nwhen the agent advertises\n{@link MultipleWorkingDirectoriesCapability.immutablePrimary} without\n{@link MultipleWorkingDirectoriesCapability.primaryReplacement} (the first\nentry is then a fixed process root), or advertises `primaryReplacement`\n(the first entry is a protected, replaceable primary slot). Individual chats\nMAY restrict to a subset via\n{@link ChatSummary.workingDirectories | their own `workingDirectories`}; a\nchat that sets none operates against this full set."
},
+ "repositorySource": {
+ "$ref": "#/$defs/URI",
+ "description": "Immutable requested source, separate from the host-resolved working directories."
+ },
+ "repositoryRevision": {
+ "type": "string",
+ "description": "Immutable requested revision, not the checkout's current HEAD."
+ },
"annotations": {
"$ref": "#/$defs/AnnotationsSummary",
"description": "Lightweight summary of this session's inline annotations channel\n(`ahp-session://annotations`). Surfaced so badge UI can render\nannotation / entry counts without subscribing. Absent when the session\ndoes not expose an annotations channel."
@@ -797,7 +827,7 @@
},
"config": {
"$ref": "#/$defs/SessionConfigState",
- "description": "Session configuration schema and current values"
+ "description": "Provider-specific session configuration schema and current values."
},
"customizations": {
"type": "array",
@@ -1066,6 +1096,14 @@
},
"description": "The working directories the session's agent has tool access to, as\nmaintained by working-directory actions. Directories are equal peers except\nwhen the agent advertises\n{@link MultipleWorkingDirectoriesCapability.immutablePrimary} without\n{@link MultipleWorkingDirectoriesCapability.primaryReplacement} (the first\nentry is then a fixed process root), or advertises `primaryReplacement`\n(the first entry is a protected, replaceable primary slot). Individual chats\nMAY restrict to a subset via\n{@link ChatSummary.workingDirectories | their own `workingDirectories`}; a\nchat that sets none operates against this full set."
},
+ "repositorySource": {
+ "$ref": "#/$defs/URI",
+ "description": "Immutable requested source, separate from the host-resolved working directories."
+ },
+ "repositoryRevision": {
+ "type": "string",
+ "description": "Immutable requested revision, not the checkout's current HEAD."
+ },
"annotations": {
"$ref": "#/$defs/AnnotationsSummary",
"description": "Lightweight summary of this session's inline annotations channel\n(`ahp-session://annotations`). Surfaced so badge UI can render\nannotation / entry counts without subscribing. Absent when the session\ndoes not expose an annotations channel."
diff --git a/scripts/generate-csharp.ts b/scripts/generate-csharp.ts
index 20721221..1f2be5bd 100644
--- a/scripts/generate-csharp.ts
+++ b/scripts/generate-csharp.ts
@@ -659,6 +659,7 @@ const STATE_STRUCTS: { name: string; omitDiscriminants?: boolean; csName?: strin
{ name: 'AgentCapabilities' },
{ name: 'MultipleChatsCapability' },
{ name: 'MultipleWorkingDirectoriesCapability' },
+ { name: 'RepositorySourceCapability' },
{ name: 'SessionModelInfo' },
{ name: 'ModelSelection' },
{ name: 'AgentSelection' },
diff --git a/scripts/generate-go.ts b/scripts/generate-go.ts
index 71926ac2..71f736b0 100644
--- a/scripts/generate-go.ts
+++ b/scripts/generate-go.ts
@@ -739,6 +739,7 @@ const STATE_STRUCTS: { name: string; omitDiscriminants?: boolean; goName?: strin
{ name: 'AgentCapabilities' },
{ name: 'MultipleChatsCapability' },
{ name: 'MultipleWorkingDirectoriesCapability' },
+ { name: 'RepositorySourceCapability' },
{ name: 'SessionModelInfo' },
{ name: 'ModelSelection' },
{ name: 'AgentSelection' },
diff --git a/scripts/generate-json-schema.test.ts b/scripts/generate-json-schema.test.ts
index 53985809..c104f4c8 100644
--- a/scripts/generate-json-schema.test.ts
+++ b/scripts/generate-json-schema.test.ts
@@ -200,6 +200,79 @@ describe('generated JSON schemas', () => {
assert.match(expiresIn.description as string, /MUST be a positive integer/);
});
+ it('keeps session config schema generic without repository metadata', () => {
+ const defs = schema.$defs as Record>;
+ const configSchema = defs.SessionConfigSchema;
+ const properties = configSchema.properties as Record>;
+ assert.deepEqual({
+ fields: Object.keys(properties).sort(),
+ required: configSchema.required,
+ propertySchema: properties.properties.additionalProperties,
+ repositoryType: defs.RepositorySessionConfig,
+ }, {
+ fields: ['properties', 'required', 'type'],
+ required: ['type', 'properties'],
+ propertySchema: { $ref: '#/$defs/SessionConfigPropertySchema' },
+ repositoryType: undefined,
+ });
+
+ const legacy = {
+ type: 'object',
+ properties: { mode: { type: 'string', title: 'Mode' } },
+ required: ['mode'],
+ };
+ assert.equal(schemaAccepts(schema, configSchema, legacy), true);
+ });
+
+ it('declares optional typed repository inputs beside generic config', () => {
+ if (file !== 'commands.schema.json') {
+ return;
+ }
+ const defs = schema.$defs as Record>;
+ for (const [definition, channel] of [
+ ['ResolveSessionConfigParams', 'ahp-root://'],
+ ['SessionConfigCompletionsParams', 'ahp-root://'],
+ ['CreateSessionParams', 'ahp-session:/repository-test'],
+ ]) {
+ const properties = defs[definition].properties as Record>;
+ assert.equal(properties.config.type, 'object');
+ assert.deepEqual(
+ Object.keys(properties).filter(name => ['repository', 'repositorySource', 'repositoryRevision'].includes(name)),
+ ['repositorySource', 'repositoryRevision'],
+ );
+ const base = { channel, ...(definition === 'SessionConfigCompletionsParams' ? { property: 'mode' } : {}) };
+ assert.equal(schemaAccepts(schema, defs[definition], base), true);
+ for (const source of [
+ {},
+ { repositorySource: 'https://example.org/team/project.git' },
+ {
+ repositorySource: 'https://example.org/team/project.git',
+ repositoryRevision: 'refs/tags/v1.2.3',
+ },
+ ]) {
+ assert.equal(schemaAccepts(schema, defs[definition], { ...base, ...source, config: { mode: 'review' } }), true);
+ }
+ for (const invalid of [{ repositorySource: 42 }, { repositorySource: null }, { repositoryRevision: 42 }]) {
+ assert.equal(schemaAccepts(schema, defs[definition], { ...base, ...invalid }), false);
+ }
+ }
+ });
+
+ it('declares immutable source metadata and an opt-in repository capability', () => {
+ const defs = schema.$defs as Record>;
+ for (const name of ['SessionState', 'SessionSummary']) {
+ const properties = defs[name].properties as Record>;
+ assert.equal(dereferenceSchema(schema, properties.repositorySource).type, 'string');
+ assert.equal(dereferenceSchema(schema, properties.repositoryRevision).type, 'string');
+ }
+ const capabilities = defs.AgentCapabilities.properties as Record>;
+ assert.deepEqual(capabilities.repositorySource.$ref, '#/$defs/RepositorySourceCapability');
+ for (const value of [{}, { repositorySource: {} }, { repositorySource: { revision: true } }]) {
+ assert.equal(schemaAccepts(schema, defs.AgentCapabilities, value), true);
+ }
+ assert.equal(schemaAccepts(schema, defs.AgentCapabilities, { repositorySource: true }), false);
+ });
+
it('constrains every ChatOrigin branch to a distinct kind', () => {
const defs = schema.$defs as Record>;
const chatOrigin = defs.ChatOrigin;
diff --git a/scripts/generate-kotlin.ts b/scripts/generate-kotlin.ts
index ea13b41e..c4e28440 100644
--- a/scripts/generate-kotlin.ts
+++ b/scripts/generate-kotlin.ts
@@ -987,6 +987,7 @@ const STATE_STRUCTS = [
'AgentCapabilities',
'MultipleChatsCapability',
'MultipleWorkingDirectoriesCapability',
+ 'RepositorySourceCapability',
'SessionModelInfo', 'ModelSelection', 'AgentSelection', 'ConfigPropertySchema', 'ConfigSchema',
'PendingMessage', 'ChatState', 'ChatSummary', 'SideChatSelection', 'SessionState', 'SessionActiveClient',
'SessionChatInputRequest', 'SessionToolConfirmationRequest', 'SessionToolClientExecutionRequest',
diff --git a/scripts/generate-rust.ts b/scripts/generate-rust.ts
index 4e999bbc..9bad0bbc 100644
--- a/scripts/generate-rust.ts
+++ b/scripts/generate-rust.ts
@@ -800,6 +800,7 @@ const STATE_STRUCTS: { name: string; omitDiscriminants?: boolean; rustName?: str
{ name: 'AgentCapabilities' },
{ name: 'MultipleChatsCapability' },
{ name: 'MultipleWorkingDirectoriesCapability' },
+ { name: 'RepositorySourceCapability' },
{ name: 'SessionModelInfo' },
{ name: 'ModelSelection' },
{ name: 'AgentSelection' },
diff --git a/scripts/generate-swift.ts b/scripts/generate-swift.ts
index 89fa9bcd..6ca96c39 100644
--- a/scripts/generate-swift.ts
+++ b/scripts/generate-swift.ts
@@ -692,6 +692,7 @@ const STATE_STRUCTS = [
'AgentCapabilities',
'MultipleChatsCapability',
'MultipleWorkingDirectoriesCapability',
+ 'RepositorySourceCapability',
'SessionModelInfo', 'ModelSelection', 'AgentSelection', 'ConfigPropertySchema', 'ConfigSchema',
'PendingMessage', 'ChatState', 'ChatSummary', 'SideChatSelection', 'SessionState', 'SessionActiveClient',
'SessionChatInputRequest', 'SessionToolConfirmationRequest', 'SessionToolClientExecutionRequest',
diff --git a/types/channels-root/commands.ts b/types/channels-root/commands.ts
index ff3c706b..00635e1c 100644
--- a/types/channels-root/commands.ts
+++ b/types/channels-root/commands.ts
@@ -79,6 +79,9 @@ export interface ListSessionsResult extends PaginatedResult {
* the full current property set (not a delta). The returned `values` contain
* server-resolved defaults to pass to `createSession`.
*
+ * This command MUST NOT clone or prepare a repository. Repository context
+ * requires the agent's `repositorySource` capability.
+ *
* @category Commands
* @method resolveSessionConfig
* @direction Client → Server
@@ -130,7 +133,11 @@ export interface ResolveSessionConfigParams extends BaseParams {
provider?: string;
/** Working directory for the session */
workingDirectory?: URI;
- /** Current user-filled configuration values */
+ /** Credential-free source context; not a working-directory URI. */
+ repositorySource?: URI;
+ /** Requested revision; requires a source and the capability's revision option. */
+ repositoryRevision?: string;
+ /** Current user-filled configuration values; see {@link SessionConfigSchema}. */
config?: Record;
}
@@ -195,6 +202,10 @@ export interface SessionConfigCompletionsParams extends BaseParams {
provider?: string;
/** Working directory for the session */
workingDirectory?: URI;
+ /** Repository context for configuration completions; this MUST NOT prepare a checkout. */
+ repositorySource?: URI;
+ /** Requested revision; requires a source and the capability's revision option. */
+ repositoryRevision?: string;
/** Current user-filled configuration values (provides context for the query) */
config?: Record;
/** Property id from the schema to query values for */
diff --git a/types/channels-root/notifications.ts b/types/channels-root/notifications.ts
index eb74fedc..8ae4aebd 100644
--- a/types/channels-root/notifications.ts
+++ b/types/channels-root/notifications.ts
@@ -175,6 +175,8 @@ export interface SessionSummaryChangedParams {
* - Like all notifications this is ephemeral and is **not** replayed on
* reconnect. A client that never receives the terminal frame SHOULD expire
* the indicator after an idle timeout.
+ * - Completion of reported work does not establish session readiness.
+ * Observe session lifecycle state for the durable outcome.
*
* @category Protocol Notifications
* @method root/progress
diff --git a/types/channels-root/state.ts b/types/channels-root/state.ts
index b6c12381..4c270025 100644
--- a/types/channels-root/state.ts
+++ b/types/channels-root/state.ts
@@ -106,6 +106,8 @@ export interface AgentInfo {
* @category Root State
*/
export interface AgentCapabilities {
+ /** The host accepts typed repository inputs for session creation and configuration queries. */
+ repositorySource?: RepositorySourceCapability;
/**
* The agent can host more than one concurrent chat per session. When absent,
* clients MUST NOT call `createChat` to open chats beyond the default one the
@@ -127,6 +129,15 @@ export interface AgentCapabilities {
multipleWorkingDirectories?: MultipleWorkingDirectoriesCapability;
}
+/**
+ * Options for repository-backed session creation.
+ * @category Root State
+ */
+export interface RepositorySourceCapability {
+ /** When true, clients may supply an explicit repositoryRevision. */
+ revision?: boolean;
+}
+
/**
* Options for the {@link AgentCapabilities.multipleChats} capability.
*
diff --git a/types/channels-session/commands.ts b/types/channels-session/commands.ts
index 5c452067..b58e5354 100644
--- a/types/channels-session/commands.ts
+++ b/types/channels-session/commands.ts
@@ -26,6 +26,9 @@ import type {
* updates. The server also broadcasts a `root/sessionAdded` notification to all
* clients.
*
+ * Repository preparation MUST finish before `session/ready` or executing turns.
+ * Clients recover the outcome from session state, not progress notifications.
+ *
* @category Commands
* @method createSession
* @direction Client → Server
@@ -67,11 +70,18 @@ export interface CreateSessionParams extends BaseParams {
* and ignores the rest. Dispatch working-directory actions to change the set
* after the session has started.
*
+ * A non-empty list and `repositorySource` are mutually exclusive.
+ * A repository URI identifies the source, not a working-directory URI; one
+ * source may produce multiple directories.
*/
workingDirectories?: URI[];
+ /** Credential-free source to prepare; requires the agent's repositorySource capability. */
+ repositorySource?: URI;
+ /** Requested branch, tag, or commit; requires a source and the capability's revision option. */
+ repositoryRevision?: string;
/**
- * Agent-specific configuration values collected via `resolveSessionConfig`.
- * Keys and values correspond to the schema returned by the server.
+ * Session configuration values collected via `resolveSessionConfig`.
+ * Keys and values follow the advertised {@link SessionConfigSchema}.
*/
config?: Record;
/**
@@ -104,6 +114,9 @@ export interface CreateSessionParams extends BaseParams {
* Disposes a session and cleans up server-side resources.
*
* The server broadcasts a `root/sessionRemoved` notification to all clients.
+ * Disposal MUST NOT erase a shared checkout or uncommitted user changes.
+ * Repository cleanup remains host-owned; ending a client's wait or subscription
+ * does not grant permission to delete repository data.
*
* @category Commands
* @method disposeSession
diff --git a/types/channels-session/state.ts b/types/channels-session/state.ts
index 78db1000..cd78b33f 100644
--- a/types/channels-session/state.ts
+++ b/types/channels-session/state.ts
@@ -139,6 +139,10 @@ export interface SessionMetadata {
* chat that sets none operates against this full set.
*/
workingDirectories?: URI[];
+ /** Immutable requested source, separate from the host-resolved working directories. */
+ repositorySource?: URI;
+ /** Immutable requested revision, not the checkout's current HEAD. */
+ repositoryRevision?: string;
/**
* Lightweight summary of this session's inline annotations channel
* (`ahp-session://annotations`). Surfaced so badge UI can render
@@ -187,7 +191,7 @@ export interface SessionState extends SessionMetadata {
* this over the session's lifetime.
*/
defaultChat?: URI;
- /** Session configuration schema and current values */
+ /** Provider-specific session configuration schema and current values. */
config?: SessionConfigState;
/**
* Top-level customizations active in this session.
diff --git a/types/test-cases/round-trips/045-session-config-without-repository.json b/types/test-cases/round-trips/045-session-config-without-repository.json
new file mode 100644
index 00000000..1b930eb0
--- /dev/null
+++ b/types/test-cases/round-trips/045-session-config-without-repository.json
@@ -0,0 +1,50 @@
+{
+ "name": "session-config-without-repository",
+ "group": "A",
+ "description": "An existing directory-backed session config remains valid without repository properties or values.",
+ "type": "Snapshot",
+ "input": {
+ "resource": "ahp-session:/directory-session",
+ "state": {
+ "provider": "example",
+ "title": "Directory session",
+ "status": 1,
+ "lifecycle": "ready",
+ "activeClients": [],
+ "chats": [],
+ "workingDirectories": ["file:///work/existing"],
+ "config": {
+ "schema": {
+ "type": "object",
+ "properties": {
+ "mode": { "type": "string", "title": "Mode", "sessionMutable": true }
+ }
+ },
+ "values": { "mode": "review" }
+ }
+ },
+ "fromSeq": 1
+ },
+ "acceptableOutputs": [{
+ "resource": "ahp-session:/directory-session",
+ "state": {
+ "provider": "example",
+ "title": "Directory session",
+ "status": 1,
+ "lifecycle": "ready",
+ "activeClients": [],
+ "chats": [],
+ "workingDirectories": ["file:///work/existing"],
+ "config": {
+ "schema": {
+ "type": "object",
+ "properties": {
+ "mode": { "type": "string", "title": "Mode", "sessionMutable": true }
+ }
+ },
+ "values": { "mode": "review" }
+ }
+ },
+ "fromSeq": 1
+ }]
+}
diff --git a/types/test-cases/round-trips/046-repository-session-source-only.json b/types/test-cases/round-trips/046-repository-session-source-only.json
new file mode 100644
index 00000000..591ba53a
--- /dev/null
+++ b/types/test-cases/round-trips/046-repository-session-source-only.json
@@ -0,0 +1,34 @@
+{
+ "name": "repository-session-source-only",
+ "group": "A",
+ "description": "A ready session preserves typed source metadata, omits the optional revision, and resolves one source to multiple directories.",
+ "type": "Snapshot",
+ "input": {
+ "resource": "ahp-session:/repository-session",
+ "state": {
+ "provider": "example",
+ "title": "Repository session",
+ "status": 1,
+ "lifecycle": "ready",
+ "activeClients": [],
+ "chats": [],
+ "repositorySource": "https://example.org/team/project.git",
+ "workingDirectories": ["file:///work/project", "file:///work/project-worktree"]
+ },
+ "fromSeq": 2
+ },
+ "acceptableOutputs": [{
+ "resource": "ahp-session:/repository-session",
+ "state": {
+ "provider": "example",
+ "title": "Repository session",
+ "status": 1,
+ "lifecycle": "ready",
+ "activeClients": [],
+ "chats": [],
+ "repositorySource": "https://example.org/team/project.git",
+ "workingDirectories": ["file:///work/project", "file:///work/project-worktree"]
+ },
+ "fromSeq": 2
+ }]
+}
diff --git a/types/test-cases/round-trips/047-repository-session-revision.json b/types/test-cases/round-trips/047-repository-session-revision.json
new file mode 100644
index 00000000..46570667
--- /dev/null
+++ b/types/test-cases/round-trips/047-repository-session-revision.json
@@ -0,0 +1,34 @@
+{
+ "name": "repository-session-revision",
+ "group": "A",
+ "description": "A creating session preserves typed source and revision metadata before a directory is resolved.",
+ "type": "Snapshot",
+ "input": {
+ "resource": "ahp-session:/repository-session",
+ "state": {
+ "provider": "example",
+ "title": "Repository session",
+ "status": 1,
+ "lifecycle": "creating",
+ "activeClients": [],
+ "chats": [],
+ "repositorySource": "https://example.org/team/project.git",
+ "repositoryRevision": "refs/tags/v1.2.3"
+ },
+ "fromSeq": 0
+ },
+ "acceptableOutputs": [{
+ "resource": "ahp-session:/repository-session",
+ "state": {
+ "provider": "example",
+ "title": "Repository session",
+ "status": 1,
+ "lifecycle": "creating",
+ "activeClients": [],
+ "chats": [],
+ "repositorySource": "https://example.org/team/project.git",
+ "repositoryRevision": "refs/tags/v1.2.3"
+ },
+ "fromSeq": 0
+ }]
+}
diff --git a/types/test-cases/round-trips/048-repository-session-failed.json b/types/test-cases/round-trips/048-repository-session-failed.json
new file mode 100644
index 00000000..36487a38
--- /dev/null
+++ b/types/test-cases/round-trips/048-repository-session-failed.json
@@ -0,0 +1,36 @@
+{
+ "name": "repository-session-failed",
+ "group": "A",
+ "description": "A failed session retains the requested repository source and revision without claiming a directory was resolved.",
+ "type": "Snapshot",
+ "input": {
+ "resource": "ahp-session:/repository-session",
+ "state": {
+ "provider": "example",
+ "title": "Repository session",
+ "status": 1,
+ "lifecycle": "failed",
+ "creationError": { "errorType": "preparationFailed", "message": "Preparation failed" },
+ "activeClients": [],
+ "chats": [],
+ "repositorySource": "https://example.org/team/project.git",
+ "repositoryRevision": "main"
+ },
+ "fromSeq": 1
+ },
+ "acceptableOutputs": [{
+ "resource": "ahp-session:/repository-session",
+ "state": {
+ "provider": "example",
+ "title": "Repository session",
+ "status": 1,
+ "lifecycle": "failed",
+ "creationError": { "errorType": "preparationFailed", "message": "Preparation failed" },
+ "activeClients": [],
+ "chats": [],
+ "repositorySource": "https://example.org/team/project.git",
+ "repositoryRevision": "main"
+ },
+ "fromSeq": 1
+ }]
+}
diff --git a/types/test-cases/round-trips/049-repository-source-capability.json b/types/test-cases/round-trips/049-repository-source-capability.json
new file mode 100644
index 00000000..89ecefbf
--- /dev/null
+++ b/types/test-cases/round-trips/049-repository-source-capability.json
@@ -0,0 +1,58 @@
+{
+ "name": "repository-source-capability",
+ "group": "A",
+ "description": "Agents advertise source-only or source-with-revision preparation independently of config schemas.",
+ "type": "InitializeResult",
+ "input": {
+ "protocolVersion": "0.9.0",
+ "serverSeq": 0,
+ "snapshots": [{
+ "resource": "ahp-root://",
+ "state": {
+ "agents": [
+ {
+ "provider": "source-only",
+ "displayName": "Source only",
+ "description": "Repository preparation",
+ "models": [],
+ "capabilities": { "repositorySource": {} }
+ },
+ {
+ "provider": "source-with-revision",
+ "displayName": "Source with revision",
+ "description": "Repository preparation",
+ "models": [],
+ "capabilities": { "repositorySource": { "revision": true } }
+ }
+ ]
+ },
+ "fromSeq": 0
+ }]
+ },
+ "acceptableOutputs": [{
+ "protocolVersion": "0.9.0",
+ "serverSeq": 0,
+ "snapshots": [{
+ "resource": "ahp-root://",
+ "state": {
+ "agents": [
+ {
+ "provider": "source-only",
+ "displayName": "Source only",
+ "description": "Repository preparation",
+ "models": [],
+ "capabilities": { "repositorySource": {} }
+ },
+ {
+ "provider": "source-with-revision",
+ "displayName": "Source with revision",
+ "description": "Repository preparation",
+ "models": [],
+ "capabilities": { "repositorySource": { "revision": true } }
+ }
+ ]
+ },
+ "fromSeq": 0
+ }]
+ }]
+}