Skip to content
Merged
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
6 changes: 3 additions & 3 deletions clients/go/ahp/reducers.go
Original file line number Diff line number Diff line change
Expand Up @@ -1422,9 +1422,9 @@ func ApplyActionToChangeset(state *ahptypes.ChangesetState, action ahptypes.Stat
}
return ReduceOutcomeNoOp

case *ahptypes.ChangesetFilesReviewedChangedAction:
ids := make(map[string]struct{}, len(a.FileIds))
for _, id := range a.FileIds {
case *ahptypes.ChangesetFilesReviewChangedAction:
ids := make(map[string]struct{}, len(a.Files))
for _, id := range a.Files {
ids[id] = struct{}{}
}
changed := false
Expand Down
49 changes: 30 additions & 19 deletions clients/go/ahptypes/actions.generated.go
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ const (
ActionTypeChangesetStatusChanged ActionType = "changeset/statusChanged"
ActionTypeChangesetFileSet ActionType = "changeset/fileSet"
ActionTypeChangesetFileRemoved ActionType = "changeset/fileRemoved"
ActionTypeChangesetFilesReviewedChanged ActionType = "changeset/filesReviewedChanged"
ActionTypeChangesetFilesReviewChanged ActionType = "changeset/filesReviewChanged"
ActionTypeChangesetContentChanged ActionType = "changeset/contentChanged"
ActionTypeChangesetOperationsChanged ActionType = "changeset/operationsChanged"
ActionTypeChangesetOperationStatusChanged ActionType = "changeset/operationStatusChanged"
Expand Down Expand Up @@ -993,21 +993,32 @@ type ChangesetFileRemovedAction struct {
FileId string `json:"fileId"`
}

// Update the {@link ChangesetFile.reviewed} flag for one or more files,
// identified by their {@link ChangesetFile.id}.
//
// Dispatched by the server as the user marks files reviewed or unreviewed
// (e.g. toggling a single file, or a "mark all as reviewed" affordance).
// Only servers that support the "review" functionality dispatch this; a
// server that leaves {@link ChangesetFile.reviewed} `undefined` never does.
//
// The reducer sets `reviewed` on every matching file and ignores any
// `fileIds` entry that does not correspond to a current file.
type ChangesetFilesReviewedChangedAction struct {
Type ActionType `json:"type"`
// The {@link ChangesetFile.id}s whose reviewed state changed.
FileIds []string `json:"fileIds"`
// The new reviewed state to apply to each listed file.
// Set the {@link ChangesetFile.reviewed} flag for one or more files — the
// GitHub-style "Viewed" toggle, applied in a single batch.
//
// Targets files by their {@link ChangesetFile.id}. Ids in {@link files} that
// do not match a file currently present in the changeset are ignored; if none
// match, the action is a no-op. Only the {@link ChangesetFile.reviewed} field
// of each matched file is affected; the files' {@link ChangesetFile.edit | edit}
// and {@link ChangesetFile._meta | _meta} are left untouched.
//
// Only meaningful on a changeset that advertises
// {@link ChangesetCapabilities.review}. Unlike every other `changeset/*` action
// this one is **client-dispatchable**: a reviewer toggles review state directly,
// applying it optimistically through the write-ahead reducer and letting the
// server echo it back on the normal `action` envelope stream. The server MAY
// also originate it (e.g. an agent marking its own output reviewed).
//
// There is no protocol-level content version, so review is not reset
// automatically when a file's contents change under a stable id. The server,
// which is the authority on what changed, resets review explicitly — either by
// re-emitting the file without `reviewed: true`, or by dispatching this action
// with `reviewed: false`.
type ChangesetFilesReviewChangedAction struct {
Type ActionType `json:"type"`
// The {@link ChangesetFile.id | ids} of the files whose review state changed.
Files []string `json:"files"`
// New review state applied to every listed file: `true` once reviewed, `false` to clear it.
Reviewed bool `json:"reviewed"`
}

Expand Down Expand Up @@ -1381,7 +1392,7 @@ func (*SessionMetaChangedAction) isStateAction() {}
func (*ChangesetStatusChangedAction) isStateAction() {}
func (*ChangesetFileSetAction) isStateAction() {}
func (*ChangesetFileRemovedAction) isStateAction() {}
func (*ChangesetFilesReviewedChangedAction) isStateAction() {}
func (*ChangesetFilesReviewChangedAction) isStateAction() {}
func (*ChangesetContentChangedAction) isStateAction() {}
func (*ChangesetOperationsChangedAction) isStateAction() {}
func (*ChangesetOperationStatusChangedAction) isStateAction() {}
Expand Down Expand Up @@ -1755,8 +1766,8 @@ func (u *StateAction) UnmarshalJSON(data []byte) error {
return err
}
u.Value = &value
case "changeset/filesReviewedChanged":
var value ChangesetFilesReviewedChangedAction
case "changeset/filesReviewChanged":
var value ChangesetFilesReviewChangedAction
if err := json.Unmarshal(data, &value); err != nil {
return err
}
Expand Down
44 changes: 40 additions & 4 deletions clients/go/ahptypes/state.generated.go
Original file line number Diff line number Diff line change
Expand Up @@ -2935,6 +2935,30 @@ type Changeset struct {
// Implementations MAY provide additional values; clients SHOULD fall back
// to a reasonable default when an unknown value is encountered.
ChangeKind string `json:"changeKind"`
// Optional capability declarations for this changeset. Absent (or an empty
// object) means the changeset advertises no optional capabilities.
//
// Because the catalogue entry is delivered up-front on
// {@link ChangesetState | the session's changeset list}, clients can decide
// whether to surface capability-gated UI (such as review checkboxes) without
// first subscribing to the changeset URI. Mirrors the presence-flag
// convention of `ClientCapabilities`.
Capabilities *ChangesetCapabilities `json:"capabilities,omitempty"`
}

// Optional capabilities a changeset advertises on its catalogue
// {@link Changeset} entry.
//
// Each field is a presence flag: an empty object `{}` means "supported",
// absence means "not supported". Sub-fields on individual capabilities are
// reserved for future per-capability options.
type ChangesetCapabilities struct {
// The changeset supports the per-file **review** workflow. When declared,
// clients MAY surface a GitHub-style "Viewed" toggle per file and dispatch
// {@link ChangesetFilesReviewChangedAction | `changeset/filesReviewChanged`} to
// set each file's {@link ChangesetFile.reviewed} flag. Clients that omit
// handling MUST treat the changeset as non-reviewable.
Review map[string]json.RawMessage `json:"review,omitempty"`
}

// Full state for a single changeset, returned when a client subscribes to
Expand Down Expand Up @@ -2964,10 +2988,22 @@ type ChangesetFile struct {
// Reuses the existing {@link FileEdit} shape. Clients derive line
// additions, deletions, and rename/create/delete semantics from this.
Edit FileEdit `json:"edit"`
// Whether the user has reviewed this file. Omit (or set to `undefined`)
// to indicate that the server does not support the "review" functionality;
// in that case clients should not surface any reviewed/unreviewed
// affordance for this file.
// Whether a reviewer has marked this file as reviewed (the GitHub-style
// "Viewed" checkbox). Absent is equivalent to `false` — clients MUST treat
// a missing value as not-yet-reviewed.
//
// Requires the changeset to advertise {@link ChangesetCapabilities.review}.
// Clients toggle it by dispatching
// {@link ChangesetFilesReviewChangedAction | `changeset/filesReviewChanged`};
// the server MAY also originate it (e.g. an agent self-reviewing its own
// output).
//
// There is no content version in the protocol, so review is **not** reset
// automatically when a file's contents change under a stable id. The server,
// which is the authority on what changed, resets review explicitly — either
// by re-emitting the file (via {@link ChangesetFileSetAction} or
// {@link ChangesetContentChangedAction}) without `reviewed: true`, or by
// dispatching `changeset/filesReviewChanged` with `reviewed: false`.
Reviewed *bool `json:"reviewed,omitempty"`
// Server-defined opaque metadata, surfaced to operations and tooling
// but not interpreted by the protocol.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1394,8 +1394,8 @@ public fun changesetReducer(state: ChangesetState, action: StateAction): Changes
}
}

is StateActionChangesetFilesReviewedChanged -> {
val ids = action.value.fileIds.toSet()
is StateActionChangesetFilesReviewChanged -> {
val ids = action.value.files.toSet()
var changed = false
val next = state.files.map { file ->
if (file.id !in ids || file.reviewed == action.value.reviewed) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -136,8 +136,8 @@ enum class ActionType {
CHANGESET_FILE_SET,
@SerialName("changeset/fileRemoved")
CHANGESET_FILE_REMOVED,
@SerialName("changeset/filesReviewedChanged")
CHANGESET_FILES_REVIEWED_CHANGED,
@SerialName("changeset/filesReviewChanged")
CHANGESET_FILES_REVIEW_CHANGED,
@SerialName("changeset/contentChanged")
CHANGESET_CONTENT_CHANGED,
@SerialName("changeset/operationsChanged")
Expand Down Expand Up @@ -1062,14 +1062,14 @@ data class ChangesetFileRemovedAction(
)

@Serializable
data class ChangesetFilesReviewedChangedAction(
data class ChangesetFilesReviewChangedAction(
val type: ActionType,
/**
* The {@link ChangesetFile.id}s whose reviewed state changed.
* The {@link ChangesetFile.id | ids} of the files whose review state changed.
*/
val fileIds: List<String>,
val files: List<String>,
/**
* The new reviewed state to apply to each listed file.
* New review state applied to every listed file: `true` once reviewed, `false` to clear it.
*/
val reviewed: Boolean
)
Expand Down Expand Up @@ -1458,7 +1458,7 @@ sealed interface StateAction
@JvmInline value class StateActionChangesetStatusChanged(val value: ChangesetStatusChangedAction) : StateAction
@JvmInline value class StateActionChangesetFileSet(val value: ChangesetFileSetAction) : StateAction
@JvmInline value class StateActionChangesetFileRemoved(val value: ChangesetFileRemovedAction) : StateAction
@JvmInline value class StateActionChangesetFilesReviewedChanged(val value: ChangesetFilesReviewedChangedAction) : StateAction
@JvmInline value class StateActionChangesetFilesReviewChanged(val value: ChangesetFilesReviewChangedAction) : StateAction
@JvmInline value class StateActionChangesetContentChanged(val value: ChangesetContentChangedAction) : StateAction
@JvmInline value class StateActionChangesetOperationsChanged(val value: ChangesetOperationsChangedAction) : StateAction
@JvmInline value class StateActionChangesetOperationStatusChanged(val value: ChangesetOperationStatusChangedAction) : StateAction
Expand Down Expand Up @@ -1552,7 +1552,7 @@ internal object StateActionSerializer : KSerializer<StateAction> {
"changeset/statusChanged" -> StateActionChangesetStatusChanged(input.json.decodeFromJsonElement(ChangesetStatusChangedAction.serializer(), element))
"changeset/fileSet" -> StateActionChangesetFileSet(input.json.decodeFromJsonElement(ChangesetFileSetAction.serializer(), element))
"changeset/fileRemoved" -> StateActionChangesetFileRemoved(input.json.decodeFromJsonElement(ChangesetFileRemovedAction.serializer(), element))
"changeset/filesReviewedChanged" -> StateActionChangesetFilesReviewedChanged(input.json.decodeFromJsonElement(ChangesetFilesReviewedChangedAction.serializer(), element))
"changeset/filesReviewChanged" -> StateActionChangesetFilesReviewChanged(input.json.decodeFromJsonElement(ChangesetFilesReviewChangedAction.serializer(), element))
"changeset/contentChanged" -> StateActionChangesetContentChanged(input.json.decodeFromJsonElement(ChangesetContentChangedAction.serializer(), element))
"changeset/operationsChanged" -> StateActionChangesetOperationsChanged(input.json.decodeFromJsonElement(ChangesetOperationsChangedAction.serializer(), element))
"changeset/operationStatusChanged" -> StateActionChangesetOperationStatusChanged(input.json.decodeFromJsonElement(ChangesetOperationStatusChangedAction.serializer(), element))
Expand Down Expand Up @@ -1639,7 +1639,7 @@ internal object StateActionSerializer : KSerializer<StateAction> {
is StateActionChangesetStatusChanged -> output.json.encodeToJsonElement(ChangesetStatusChangedAction.serializer(), value.value)
is StateActionChangesetFileSet -> output.json.encodeToJsonElement(ChangesetFileSetAction.serializer(), value.value)
is StateActionChangesetFileRemoved -> output.json.encodeToJsonElement(ChangesetFileRemovedAction.serializer(), value.value)
is StateActionChangesetFilesReviewedChanged -> output.json.encodeToJsonElement(ChangesetFilesReviewedChangedAction.serializer(), value.value)
is StateActionChangesetFilesReviewChanged -> output.json.encodeToJsonElement(ChangesetFilesReviewChangedAction.serializer(), value.value)
is StateActionChangesetContentChanged -> output.json.encodeToJsonElement(ChangesetContentChangedAction.serializer(), value.value)
is StateActionChangesetOperationsChanged -> output.json.encodeToJsonElement(ChangesetOperationsChangedAction.serializer(), value.value)
is StateActionChangesetOperationStatusChanged -> output.json.encodeToJsonElement(ChangesetOperationStatusChangedAction.serializer(), value.value)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3986,7 +3986,30 @@ data class Changeset(
* Implementations MAY provide additional values; clients SHOULD fall back
* to a reasonable default when an unknown value is encountered.
*/
val changeKind: String
val changeKind: String,
/**
* Optional capability declarations for this changeset. Absent (or an empty
* object) means the changeset advertises no optional capabilities.
*
* Because the catalogue entry is delivered up-front on
* {@link ChangesetState | the session's changeset list}, clients can decide
* whether to surface capability-gated UI (such as review checkboxes) without
* first subscribing to the changeset URI. Mirrors the presence-flag
* convention of `ClientCapabilities`.
*/
val capabilities: ChangesetCapabilities? = null
)

@Serializable
data class ChangesetCapabilities(
/**
* The changeset supports the per-file **review** workflow. When declared,
* clients MAY surface a GitHub-style "Viewed" toggle per file and dispatch
* {@link ChangesetFilesReviewChangedAction | `changeset/filesReviewChanged`} to
* set each file's {@link ChangesetFile.reviewed} flag. Clients that omit
* handling MUST treat the changeset as non-reviewable.
*/
val review: Map<String, JsonElement>? = null
)

@Serializable
Expand Down Expand Up @@ -4023,10 +4046,22 @@ data class ChangesetFile(
*/
val edit: FileEdit,
/**
* Whether the user has reviewed this file. Omit (or set to `undefined`)
* to indicate that the server does not support the "review" functionality;
* in that case clients should not surface any reviewed/unreviewed
* affordance for this file.
* Whether a reviewer has marked this file as reviewed (the GitHub-style
* "Viewed" checkbox). Absent is equivalent to `false` — clients MUST treat
* a missing value as not-yet-reviewed.
*
* Requires the changeset to advertise {@link ChangesetCapabilities.review}.
* Clients toggle it by dispatching
* {@link ChangesetFilesReviewChangedAction | `changeset/filesReviewChanged`};
* the server MAY also originate it (e.g. an agent self-reviewing its own
* output).
*
* There is no content version in the protocol, so review is **not** reset
* automatically when a file's contents change under a stable id. The server,
* which is the authority on what changed, resets review explicitly — either
* by re-emitting the file (via {@link ChangesetFileSetAction} or
* {@link ChangesetContentChangedAction}) without `reviewed: true`, or by
* dispatching `changeset/filesReviewChanged` with `reviewed: false`.
*/
val reviewed: Boolean? = null,
/**
Expand Down
43 changes: 27 additions & 16 deletions clients/rust/crates/ahp-types/src/actions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -137,8 +137,8 @@ pub enum ActionType {
ChangesetFileSet,
#[serde(rename = "changeset/fileRemoved")]
ChangesetFileRemoved,
#[serde(rename = "changeset/filesReviewedChanged")]
ChangesetFilesReviewedChanged,
#[serde(rename = "changeset/filesReviewChanged")]
ChangesetFilesReviewChanged,
#[serde(rename = "changeset/contentChanged")]
ChangesetContentChanged,
#[serde(rename = "changeset/operationsChanged")]
Expand Down Expand Up @@ -1199,22 +1199,33 @@ pub struct ChangesetFileRemovedAction {
pub file_id: String,
}

/// Update the {@link ChangesetFile.reviewed} flag for one or more files,
/// identified by their {@link ChangesetFile.id}.
/// Set the {@link ChangesetFile.reviewed} flag for one or more files — the
/// GitHub-style "Viewed" toggle, applied in a single batch.
///
/// Dispatched by the server as the user marks files reviewed or unreviewed
/// (e.g. toggling a single file, or a "mark all as reviewed" affordance).
/// Only servers that support the "review" functionality dispatch this; a
/// server that leaves {@link ChangesetFile.reviewed} `undefined` never does.
/// Targets files by their {@link ChangesetFile.id}. Ids in {@link files} that
/// do not match a file currently present in the changeset are ignored; if none
/// match, the action is a no-op. Only the {@link ChangesetFile.reviewed} field
/// of each matched file is affected; the files' {@link ChangesetFile.edit | edit}
/// and {@link ChangesetFile._meta | _meta} are left untouched.
///
/// The reducer sets `reviewed` on every matching file and ignores any
/// `fileIds` entry that does not correspond to a current file.
/// Only meaningful on a changeset that advertises
/// {@link ChangesetCapabilities.review}. Unlike every other `changeset/*` action
/// this one is **client-dispatchable**: a reviewer toggles review state directly,
/// applying it optimistically through the write-ahead reducer and letting the
/// server echo it back on the normal `action` envelope stream. The server MAY
/// also originate it (e.g. an agent marking its own output reviewed).
///
/// There is no protocol-level content version, so review is not reset
/// automatically when a file's contents change under a stable id. The server,
/// which is the authority on what changed, resets review explicitly — either by
/// re-emitting the file without `reviewed: true`, or by dispatching this action
/// with `reviewed: false`.
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct ChangesetFilesReviewedChangedAction {
/// The {@link ChangesetFile.id}s whose reviewed state changed.
pub file_ids: Vec<String>,
/// The new reviewed state to apply to each listed file.
pub struct ChangesetFilesReviewChangedAction {
/// The {@link ChangesetFile.id | ids} of the files whose review state changed.
pub files: Vec<String>,
/// New review state applied to every listed file: `true` once reviewed, `false` to clear it.
pub reviewed: bool,
}

Expand Down Expand Up @@ -1709,8 +1720,8 @@ pub enum StateAction {
ChangesetFileSet(ChangesetFileSetAction),
#[serde(rename = "changeset/fileRemoved")]
ChangesetFileRemoved(ChangesetFileRemovedAction),
#[serde(rename = "changeset/filesReviewedChanged")]
ChangesetFilesReviewedChanged(ChangesetFilesReviewedChangedAction),
#[serde(rename = "changeset/filesReviewChanged")]
ChangesetFilesReviewChanged(ChangesetFilesReviewChangedAction),
#[serde(rename = "changeset/contentChanged")]
ChangesetContentChanged(Box<ChangesetContentChangedAction>),
#[serde(rename = "changeset/operationsChanged")]
Expand Down
Loading
Loading