Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ A spec change ripples into every `clients/<lang>/` mirror, so you often need to
| Toolchain | Image | Notes |
| --- | --- | --- |
| JDK 17 (Kotlin client) | `docker.io/library/eclipse-temurin:17-jdk` | Gradle comes from the repo's `./gradlew` wrapper. |
| .NET SDK 10 (.NET client) | `mcr.microsoft.com/dotnet/sdk:10.0` | Supports `.slnx`; the test host rolls forward from .NET 8. |

Mount the **repo root** (the shared conformance fixtures under `types/test-cases/` are resolved by walking up from the working directory, so a client-dir-only mount fails) and keep build caches out of `$HOME`. Example — run the Kotlin client's tests:

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1212,9 +1212,7 @@ public sealed record SessionDefaultChatChangedAction
public string? DefaultChat { get; init; }
}

/// <summary>A new message has been sent to the agent, and a new turn starts.
///
/// A client is only allowed to send {@link MessageKind.User} messages.</summary>
/// <summary>A new message has been sent to the agent, and a new turn starts.</summary>
public sealed record ChatTurnStartedAction
{
public ActionType Type { get; init; } = ActionType.ChatTurnStarted;
Expand Down Expand Up @@ -1988,9 +1986,7 @@ public sealed record ChatTurnsLoadedAction
/// message and kind in place.
///
/// If the chat is idle when a queued message is set, the server SHOULD
/// immediately consume it and start a new turn.
///
/// A client is only allowed to send {@link MessageKind.User} messages.</summary>
/// immediately consume it and start a new turn.</summary>
public sealed record ChatPendingMessageSetAction
{
public ActionType Type { get; init; } = ActionType.ChatPendingMessageSet;
Expand Down
4 changes: 0 additions & 4 deletions clients/go/ahptypes/actions.generated.go
Original file line number Diff line number Diff line change
Expand Up @@ -251,8 +251,6 @@ type SessionDefaultChatChangedAction struct {
}

// A new message has been sent to the agent, and a new turn starts.
//
// A client is only allowed to send {@link MessageKind.User} messages.
type ChatTurnStartedAction struct {
Type ActionType `json:"type"`
// Turn identifier
Expand Down Expand Up @@ -789,8 +787,6 @@ type ChatReasoningAction struct {
//
// If the chat is idle when a queued message is set, the server SHOULD
// immediately consume it and start a new turn.
//
// A client is only allowed to send {@link MessageKind.User} messages.
type ChatPendingMessageSetAction struct {
Type ActionType `json:"type"`
// Whether this is a steering or queued message
Expand Down
4 changes: 0 additions & 4 deletions clients/rust/crates/ahp-types/src/actions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -590,8 +590,6 @@ pub struct SessionDefaultChatChangedAction {
}

/// A new message has been sent to the agent, and a new turn starts.
///
/// A client is only allowed to send {@link MessageKind.User} messages.
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct ChatTurnStartedAction {
Expand Down Expand Up @@ -1461,8 +1459,6 @@ pub struct SessionInputNeededRemovedAction {
///
/// If the chat is idle when a queued message is set, the server SHOULD
/// immediately consume it and start a new turn.
///
/// A client is only allowed to send {@link MessageKind.User} messages.
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct ChatPendingMessageSetAction {
Expand Down
5 changes: 5 additions & 0 deletions docs/.changes/20261029-non-user-chat-input.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"type": "changed",
"message": "Remove the User-only restriction on client-submitted messages in `chat/turnStarted` and `chat/pendingMessageSet`.",
"issues": [542]
}
4 changes: 2 additions & 2 deletions schema/actions.schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -747,7 +747,7 @@
},
"ChatTurnStartedAction": {
"type": "object",
"description": "A new message has been sent to the agent, and a new turn starts.\n\nA client is only allowed to send {@link MessageKind.User} messages.",
"description": "A new message has been sent to the agent, and a new turn starts.",
"properties": {
"type": {
"const": "chat/turnStarted"
Expand Down Expand Up @@ -1595,7 +1595,7 @@
},
"ChatPendingMessageSetAction": {
"type": "object",
"description": "A pending message was set (upsert semantics: creates or replaces).\n\nBoth kinds are stored in {@link ChatState.queuedMessages}, including this\naction's `kind`. A new `id` appends an entry; an existing `id` replaces its\nmessage and kind in place.\n\nIf the chat is idle when a queued message is set, the server SHOULD\nimmediately consume it and start a new turn.\n\nA client is only allowed to send {@link MessageKind.User} messages.",
"description": "A pending message was set (upsert semantics: creates or replaces).\n\nBoth kinds are stored in {@link ChatState.queuedMessages}, including this\naction's `kind`. A new `id` appends an entry; an existing `id` replaces its\nmessage and kind in place.\n\nIf the chat is idle when a queued message is set, the server SHOULD\nimmediately consume it and start a new turn.",
"properties": {
"type": {
"const": "chat/pendingMessageSet"
Expand Down
4 changes: 2 additions & 2 deletions schema/commands.schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -8783,7 +8783,7 @@
},
"ChatTurnStartedAction": {
"type": "object",
"description": "A new message has been sent to the agent, and a new turn starts.\n\nA client is only allowed to send {@link MessageKind.User} messages.",
"description": "A new message has been sent to the agent, and a new turn starts.",
"properties": {
"type": {
"const": "chat/turnStarted"
Expand Down Expand Up @@ -9631,7 +9631,7 @@
},
"ChatPendingMessageSetAction": {
"type": "object",
"description": "A pending message was set (upsert semantics: creates or replaces).\n\nBoth kinds are stored in {@link ChatState.queuedMessages}, including this\naction's `kind`. A new `id` appends an entry; an existing `id` replaces its\nmessage and kind in place.\n\nIf the chat is idle when a queued message is set, the server SHOULD\nimmediately consume it and start a new turn.\n\nA client is only allowed to send {@link MessageKind.User} messages.",
"description": "A pending message was set (upsert semantics: creates or replaces).\n\nBoth kinds are stored in {@link ChatState.queuedMessages}, including this\naction's `kind`. A new `id` appends an entry; an existing `id` replaces its\nmessage and kind in place.\n\nIf the chat is idle when a queued message is set, the server SHOULD\nimmediately consume it and start a new turn.",
"properties": {
"type": {
"const": "chat/pendingMessageSet"
Expand Down
4 changes: 2 additions & 2 deletions schema/errors.schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -9953,7 +9953,7 @@
},
"ChatTurnStartedAction": {
"type": "object",
"description": "A new message has been sent to the agent, and a new turn starts.\n\nA client is only allowed to send {@link MessageKind.User} messages.",
"description": "A new message has been sent to the agent, and a new turn starts.",
"properties": {
"type": {
"const": "chat/turnStarted"
Expand Down Expand Up @@ -10690,7 +10690,7 @@
},
"ChatPendingMessageSetAction": {
"type": "object",
"description": "A pending message was set (upsert semantics: creates or replaces).\n\nBoth kinds are stored in {@link ChatState.queuedMessages}, including this\naction's `kind`. A new `id` appends an entry; an existing `id` replaces its\nmessage and kind in place.\n\nIf the chat is idle when a queued message is set, the server SHOULD\nimmediately consume it and start a new turn.\n\nA client is only allowed to send {@link MessageKind.User} messages.",
"description": "A pending message was set (upsert semantics: creates or replaces).\n\nBoth kinds are stored in {@link ChatState.queuedMessages}, including this\naction's `kind`. A new `id` appends an entry; an existing `id` replaces its\nmessage and kind in place.\n\nIf the chat is idle when a queued message is set, the server SHOULD\nimmediately consume it and start a new turn.",
"properties": {
"type": {
"const": "chat/pendingMessageSet"
Expand Down
4 changes: 0 additions & 4 deletions types/channels-chat/actions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -62,8 +62,6 @@ interface ToolCallActionBase {
/**
* A new message has been sent to the agent, and a new turn starts.
*
* A client is only allowed to send {@link MessageKind.User} messages.
*
* @category Chat Actions
* @version 1
* @clientDispatchable
Expand Down Expand Up @@ -777,8 +775,6 @@ export interface ChatTurnsLoadedAction {
* If the chat is idle when a queued message is set, the server SHOULD
* immediately consume it and start a new turn.
*
* A client is only allowed to send {@link MessageKind.User} messages.
*
* @category Chat Actions
* @version 1
* @clientDispatchable
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
{
"description": "non-user turn input preserves authorship and payloads in active turns and completed history without fabricated responses",
"reducer": "chat",
"initial": {
"resource": "ahp-chat:/non-user",
"title": "Non-user input",
"status": 1,
"modifiedAt": "2026-10-29T00:00:00.000Z",
"turns": []
},
"actions": [
{
"type": "chat/turnStarted",
"turnId": "agent-turn",
"startedAt": "2026-10-29T00:00:00.000Z",
"message": {
"text": "The worker finished its analysis.",
"origin": { "kind": "agent" },
"attachments": [
{ "type": "simple", "label": "Worker result", "modelRepresentation": "The analysis passed.", "_meta": { "resultId": "result-1" } }
],
"model": { "id": "model-1" },
"agent": { "uri": "ahp-agent:/reviewer" },
"_meta": { "sourceChat": "ahp-chat:/worker" }
}
},
{ "type": "chat/turnComplete", "turnId": "agent-turn", "duration": 1000 },
{
"type": "chat/turnStarted",
"turnId": "notification-turn",
"startedAt": "2026-10-29T00:00:01.000Z",
"message": {
"text": "The worker conversation is now idle.",
"origin": { "kind": "systemNotification" }
}
},
{ "type": "chat/turnComplete", "turnId": "notification-turn", "duration": 0 },
{
"type": "chat/turnStarted",
"turnId": "tool-turn",
"startedAt": "2026-10-29T00:00:02.000Z",
"message": {
"text": "Background work completed.",
"origin": { "kind": "tool" }
}
}
],
"expected": {
"resource": "ahp-chat:/non-user",
"title": "Non-user input",
"status": 8,
"modifiedAt": "2026-10-29T00:00:02.000Z",
"turns": [
{
"id": "agent-turn",
"startedAt": "2026-10-29T00:00:00.000Z",
"duration": 1000,
"message": {
"text": "The worker finished its analysis.",
"origin": { "kind": "agent" },
"attachments": [
{ "type": "simple", "label": "Worker result", "modelRepresentation": "The analysis passed.", "_meta": { "resultId": "result-1" } }
],
"model": { "id": "model-1" },
"agent": { "uri": "ahp-agent:/reviewer" },
"_meta": { "sourceChat": "ahp-chat:/worker" }
},
"responseParts": [],
"usage": null,
"state": "complete"
},
{
"id": "notification-turn",
"startedAt": "2026-10-29T00:00:01.000Z",
"duration": 0,
"message": {
"text": "The worker conversation is now idle.",
"origin": { "kind": "systemNotification" }
},
"responseParts": [],
"usage": null,
"state": "complete"
}
],
"activeTurn": {
"id": "tool-turn",
"startedAt": "2026-10-29T00:00:02.000Z",
"message": {
"text": "Background work completed.",
"origin": { "kind": "tool" }
},
"responseParts": [],
"usage": null
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
{
"description": "non-user queued and steering input preserves authorship through upserts and queued turn consumption",
"reducer": "chat",
"initial": {
"resource": "ahp-chat:/non-user",
"title": "Non-user input",
"status": 1,
"modifiedAt": "2026-10-29T00:00:00.000Z",
"turns": []
},
"actions": [
{
"type": "chat/pendingMessageSet",
"kind": "queued",
"id": "agent-result",
"message": {
"text": "The worker finished its analysis.",
"origin": { "kind": "agent" },
"model": { "id": "model-1" },
"agent": { "uri": "ahp-agent:/reviewer" }
}
},
{
"type": "chat/pendingMessageSet",
"kind": "queued",
"id": "tool-result",
"message": { "text": "Background work completed.", "origin": { "kind": "tool" } }
},
{
"type": "chat/pendingMessageSet",
"kind": "steering",
"id": "notification",
"message": { "text": "Worker finishing.", "origin": { "kind": "systemNotification" } }
},
{
"type": "chat/pendingMessageSet",
"kind": "steering",
"id": "notification",
"message": { "text": "Worker finished.", "origin": { "kind": "systemNotification" }, "_meta": { "workerId": "worker-1" } }
},
{
"type": "chat/pendingMessageSet",
"kind": "steering",
"id": "notification",
"message": { "text": "Worker finished.", "origin": { "kind": "systemNotification" }, "_meta": { "workerId": "worker-1" } }
},
{
"type": "chat/turnStarted",
"turnId": "agent-turn",
"startedAt": "2026-10-29T00:00:01.000Z",
"queuedMessageId": "agent-result",
"message": {
"text": "The worker finished its analysis.",
"origin": { "kind": "agent" },
"model": { "id": "model-1" },
"agent": { "uri": "ahp-agent:/reviewer" }
}
}
],
"expected": {
"resource": "ahp-chat:/non-user",
"title": "Non-user input",
"status": 8,
"modifiedAt": "2026-10-29T00:00:01.000Z",
"turns": [],
"activeTurn": {
"id": "agent-turn",
"startedAt": "2026-10-29T00:00:01.000Z",
"message": {
"text": "The worker finished its analysis.",
"origin": { "kind": "agent" },
"model": { "id": "model-1" },
"agent": { "uri": "ahp-agent:/reviewer" }
},
"responseParts": [],
"usage": null
},
"queuedMessages": [
{
"id": "tool-result",
"kind": "queued",
"message": { "text": "Background work completed.", "origin": { "kind": "tool" } }
},
{
"id": "notification",
"kind": "steering",
"message": { "text": "Worker finished.", "origin": { "kind": "systemNotification" }, "_meta": { "workerId": "worker-1" } }
}
],
"steeringMessage": {
"id": "notification",
"kind": "steering",
"message": { "text": "Worker finished.", "origin": { "kind": "systemNotification" }, "_meta": { "workerId": "worker-1" } }
}
}
}
Loading