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
15 changes: 15 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,21 @@ Spec version: `0.3.0`
- Added optional `changes` field of type `ChangesSummary` to `SessionSummary`,
carrying optional `additions`, `deletions`, and `files` counts so servers
can advertise an at-a-glance view of a session's file-change footprint.
- Added a new annotations channel exposed on `ahp-session:/<uuid>/annotations`.
Annotations anchor to a `(turnId, resource)` pair with an optional `range`
(omitted to anchor to the entire file), carry a `resolved` flag (newly
created annotations start unresolved), and always carry at least one entry.
Clients drive every mutation by dispatching the client-dispatchable
`annotations/set`, `annotations/removed`, `annotations/entrySet`, and
`annotations/entryRemoved` state actions directly — assigning the
`Annotation.id` / `AnnotationEntry.id` themselves — rather than through RPC
commands, so annotations inherit write-ahead replay and conflict resolution.
`SessionSummary.annotations` advertises the per-session `AnnotationsSummary`
(`{ resource, annotationCount, entryCount }`) for badge UI.
- Added an `annotations` `MessageAttachment` variant
(`MessageAnnotationsAttachment`) that references annotations on a
session's annotations channel by its `resource` URI, optionally narrowed to
an `annotationIds` array (omitted to reference every annotation).
- Removed the `additions`, `deletions`, and `files` fields from
`ChangesetSummary`. Aggregate counts now live on `SessionSummary.changes`;
per-changeset views derive their own totals from `ChangesetState.files`.
Expand Down
12 changes: 12 additions & 0 deletions clients/go/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,18 @@ Implements AHP 0.3.0.
`idle → running → error` lifecycle of a changeset operation.
- `AgentCustomization._meta` provider metadata field.
- Optional `changes` field on `SessionSummary` (`ChangesSummary` with optional `additions`, `deletions`, and `files` counts) summarising a session's file-change footprint.
- New annotations channel wire types (`ahp-session:/<uuid>/annotations`):
`AnnotationsState`, `Annotation`, `AnnotationEntry`,
`AnnotationsSummary`; the client-dispatchable `AnnotationsSetAction`,
`AnnotationsRemovedAction`, `AnnotationsEntrySetAction`,
`AnnotationsEntryRemovedAction` variants — clients drive every annotation
mutation by dispatching these directly, assigning the `Annotation.Id` /
`AnnotationEntry.Id` themselves;
`ApplyActionToAnnotations` (stub mirroring `ApplyActionToChangeset`); and
`SnapshotState.Annotations`.
- `MessageAnnotationsAttachment` (`annotations` `MessageAttachment` variant)
referencing annotations on a session's annotations channel by `Resource`
URI, optionally narrowed to an `AnnotationIds` array.


### Changed
Expand Down
18 changes: 18 additions & 0 deletions clients/go/ahp/reducers.go
Original file line number Diff line number Diff line change
Expand Up @@ -1166,3 +1166,21 @@ func ApplyActionToChangeset(state *ahptypes.ChangesetState, action ahptypes.Stat
}
return ReduceOutcomeOutOfScope
}

// ─── Annotations Reducer ─────────────────────────────────────────

// ApplyActionToAnnotations is the entry point for annotations actions.
// Mirrors the Rust client's stub: every recognized annotations action
// short-circuits as [ReduceOutcomeNoOp] until the full annotations
// reducer is ported. Unrelated actions return [ReduceOutcomeOutOfScope].
func ApplyActionToAnnotations(state *ahptypes.AnnotationsState, action ahptypes.StateAction) ReduceOutcome {
_ = state
switch action.Value.(type) {
case *ahptypes.AnnotationsSetAction,
*ahptypes.AnnotationsRemovedAction,
*ahptypes.AnnotationsEntrySetAction,
*ahptypes.AnnotationsEntryRemovedAction:
return ReduceOutcomeNoOp
}
return ReduceOutcomeOutOfScope
}
3 changes: 3 additions & 0 deletions clients/go/ahp/reducers_fixture_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,9 @@ func TestFixtureDrivenReducerParity(t *testing.T) {
case "changeset":
// Changeset reducer logic is deferred — skip.
tt.Skip("changeset reducer is a stub in this client (parity with Rust)")
case "annotations":
// Annotations reducer logic is deferred — skip.
tt.Skip("annotations reducer is a stub in this client (parity with Rust)")
case "resourceWatch":
// Resource-watch reducer logic is deferred — skip.
tt.Skip("resourceWatch reducer is a stub in this client (parity with Rust)")
Expand Down
89 changes: 89 additions & 0 deletions clients/go/ahptypes/actions.generated.go
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,10 @@ const (
ActionTypeChangesetOperationsChanged ActionType = "changeset/operationsChanged"
ActionTypeChangesetOperationStatusChanged ActionType = "changeset/operationStatusChanged"
ActionTypeChangesetCleared ActionType = "changeset/cleared"
ActionTypeAnnotationsSet ActionType = "annotations/set"
ActionTypeAnnotationsRemoved ActionType = "annotations/removed"
ActionTypeAnnotationsEntrySet ActionType = "annotations/entrySet"
ActionTypeAnnotationsEntryRemoved ActionType = "annotations/entryRemoved"
ActionTypeRootTerminalsChanged ActionType = "root/terminalsChanged"
ActionTypeRootConfigChanged ActionType = "root/configChanged"
ActionTypeTerminalData ActionType = "terminal/data"
Expand Down Expand Up @@ -796,6 +800,63 @@ type ChangesetClearedAction struct {
Type ActionType `json:"type"`
}

// Upsert an {@link Annotation} in the annotations channel — adds a new
// annotation, or replaces an existing one identified by
// {@link Annotation.id}.
//
// Dispatched by a client to create an annotation (together with its
// mandatory first entry) or to re-anchor / resolve an existing one; the
// dispatching client assigns the {@link Annotation.id} and the id of any
// new entry. When replacing, the full annotation payload (including its
// {@link Annotation.entries | entries} list) is substituted; producers
// SHOULD prefer {@link AnnotationsEntrySetAction} for per-entry edits to
// keep wire updates small.
type AnnotationsSetAction struct {
Type ActionType `json:"type"`
// The new or replacement annotation. MUST contain at least one entry.
Annotation Annotation `json:"annotation"`
}

// Remove an {@link Annotation} from the channel by its id.
//
// Dispatched to delete an entire annotation and every entry it contains.
// Because the protocol forbids empty annotations, a client that wants to
// remove the last remaining entry dispatches this action — collapsing the
// annotation — rather than {@link AnnotationsEntryRemovedAction}.
type AnnotationsRemovedAction struct {
Type ActionType `json:"type"`
// The {@link Annotation.id} of the annotation to remove.
AnnotationId string `json:"annotationId"`
}

// Upsert an {@link AnnotationEntry} within an existing annotation — adds a
// new entry, or replaces one identified by {@link AnnotationEntry.id}. The
// dispatching client assigns the {@link AnnotationEntry.id} of a new entry.
// If {@link annotationId} does not match any current annotation the action
// is a no-op.
type AnnotationsEntrySetAction struct {
Type ActionType `json:"type"`
// The {@link Annotation.id} the entry belongs to.
AnnotationId string `json:"annotationId"`
// The new or replacement entry.
Entry AnnotationEntry `json:"entry"`
}

// Remove a single {@link AnnotationEntry} from an annotation without
// collapsing the annotation itself. Used when more than one entry remains —
// to remove the last entry a client dispatches {@link AnnotationsRemovedAction}
// instead, since the protocol forbids empty annotations.
//
// If either {@link annotationId} or {@link entryId} does not match the
// current state the action is a no-op.
type AnnotationsEntryRemovedAction struct {
Type ActionType `json:"type"`
// The {@link Annotation.id} the entry belongs to.
AnnotationId string `json:"annotationId"`
// The {@link AnnotationEntry.id} to remove.
EntryId string `json:"entryId"`
}

// Fired when the list of known terminals changes.
//
// Full-replacement semantics: the `terminals` array replaces the previous
Expand Down Expand Up @@ -1001,6 +1062,10 @@ func (*ChangesetFileRemovedAction) isStateAction() {}
func (*ChangesetOperationsChangedAction) isStateAction() {}
func (*ChangesetOperationStatusChangedAction) isStateAction() {}
func (*ChangesetClearedAction) isStateAction() {}
func (*AnnotationsSetAction) isStateAction() {}
func (*AnnotationsRemovedAction) isStateAction() {}
func (*AnnotationsEntrySetAction) isStateAction() {}
func (*AnnotationsEntryRemovedAction) isStateAction() {}
func (*RootTerminalsChangedAction) isStateAction() {}
func (*TerminalDataAction) isStateAction() {}
func (*TerminalInputAction) isStateAction() {}
Expand Down Expand Up @@ -1329,6 +1394,30 @@ func (u *StateAction) UnmarshalJSON(data []byte) error {
return err
}
u.Value = &value
case "annotations/set":
var value AnnotationsSetAction
if err := json.Unmarshal(data, &value); err != nil {
return err
}
u.Value = &value
case "annotations/removed":
var value AnnotationsRemovedAction
if err := json.Unmarshal(data, &value); err != nil {
return err
}
u.Value = &value
case "annotations/entrySet":
var value AnnotationsEntrySetAction
if err := json.Unmarshal(data, &value); err != nil {
return err
}
u.Value = &value
case "annotations/entryRemoved":
var value AnnotationsEntryRemovedAction
if err := json.Unmarshal(data, &value); err != nil {
return err
}
u.Value = &value
case "root/terminalsChanged":
var value RootTerminalsChangedAction
if err := json.Unmarshal(data, &value); err != nil {
Expand Down
5 changes: 5 additions & 0 deletions clients/go/ahptypes/notifications.generated.go
Original file line number Diff line number Diff line change
Expand Up @@ -189,4 +189,9 @@ type PartialSessionSummary struct {
// session's footprint (e.g., for list rendering) without requiring the
// client to subscribe to a changeset.
Changes *ChangesSummary `json:"changes,omitempty"`
// 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
// does not expose an annotations channel.
Annotations *AnnotationsSummary `json:"annotations,omitempty"`
}
Loading
Loading