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
24 changes: 24 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,30 @@ All notable changes to this project are documented in this file. Format follows

For narrative release notes written for operators and product owners, see [RELEASE_NOTES.md](RELEASE_NOTES.md).

## [1.19.0] - 2026-09-06

A correctness release for interrupted turns, plus the share inbox coming out of the dark. Two separate defects made a conversation misreport its own history: a completed response could be labelled **"Response interrupted"** with a Continue button, and an interrupted one could show the model-directed `<interruption_note>` in the user's own chat bubble — permanently. Both are fixed at the source rather than patched at the render. The artifact **"Shared with you" inbox now ships on by default** with a kill switch, so a fork gets the finished feature instead of having to discover a variable. Infrastructure adds `UserArtifactsIndex` to the existing `{prefix}-user-artifacts` table (one GSI operation) with a backfill for rows that predate it; nothing reads the index yet. **Requires a CDK deploy**, and two one-shot scripts are available post-deploy.

### 🚀 Added

- **Open the full announcement from the banner.** The banner text is now a control that opens the announcement's modal, so a notice too long for one line is readable without hunting for the What's New panel (#987)
- **`UserArtifactsIndex`** on the existing `{prefix}-user-artifacts` table (`GSI2PK=USER#{uid}`, `GSI2SK=ARTIFACT#{updated_at}#{aid}`), sparse over HEAD rows, plus `scripts/backfill_artifact_user_index_keys.py` to stamp rows written before 2026-09-04. The artifact writer stamps the keys on both write paths. The library listing still reads the base table in this release (#982)
- **`scripts/backfill_false_interrupted_markers.py`** — one-shot cleanup for the stale interrupted-turn markers left by the defect below. Dry-run by default; clears only `navigated_away` markers written more than 900s after the session's last message (#988)

### ⚠️ Changed

- **The artifact "Shared with you" inbox now defaults ON** with a kill switch (`ARTIFACT_SHARE_INBOX_ENABLED=false` to disable), reversing the opt-in default it shipped with in 1.18.0. The surface landed before the product decision did; that decision has now been made, and an opt-in default would silently cost every fork a finished feature (#986)

### 🐛 Fixed

- **Completed responses were being labelled "Response interrupted"**, with a Continue button that would resume an already-finished answer. The SPA released a session's `AbortController` only on the Stop button, never on normal stream teardown, so every finished turn still looked in-flight for the life of the tab and the next refresh or tab close attributed a `navigated_away` interruption to it — one departure marking every session streamed in that tab. The controller is now released on teardown, and `POST /sessions/{id}/interrupt` verifies a turn is genuinely in flight against the session's single-flight lease before recording a departure (#988)
- **An interrupted turn showed the model-directed `<interruption_note>` in the user's own chat bubble.** `displayText` — the clean copy of what the user typed — was written only by the stream coordinator's success path, so any turn that was stopped, dropped, or errored left the augmented prompt as the only text the UI could render. It is now written on `MessageAddedEvent`, when the user's message enters history and before the model call, so every exit path has it. Affected every prompt augmentation (RAG context, attachment guidance, MCP App context), not just interruption notes, and every model (#990)
- **The sidebar showed "No Chats Yet" while sessions were still loading.** The loading test read `value() === undefined`, but the resource short-circuits to `null` on the ordinary cold-start path — `SessionService` is constructed during `APP_INITIALIZER` before the BFF bootstrap resolves — and `reload()` preserves that `null` through the real fetch (#985)

### 🏗️ Infrastructure

- New `UserArtifactsIndex` GSI on `{prefix}-user-artifacts`. **One** index operation on an existing table, within the DynamoDB `UpdateTable` limit; `infrastructure/gsi-inventory.json` records it (#982)

## [1.18.0] - 2026-09-06

Artifacts stop being a per-conversation curiosity and become a place users go. There is a library at `/artifacts` with previews, rename, delete and an in-app viewer; artifacts can be **shared** with named people or the whole tenant; and sharing a conversation now shares the artifacts in it, which previously left the recipient staring at nothing where the owner saw cards. Alongside it, two more surfaces the platform had no way to do at all: **feature announcements** — an admin-authored What's New feed with a banner, a modal and per-announcement reach stats — and **mid-turn steering**, which lets a follow-up typed while the model is still working land inside the running turn at the next tool boundary instead of interrupting it. The SPA gains a **single-file rebranding surface** so a fork can change app name, greeting, logo and the entire color system without touching a component. On the cost side the GPT-5.6 family (Sol / Terra / Luna) is curated with verified rates, and every published GPT-5.6 rate in the catalog is corrected. **Requires a CDK deploy** (new `{prefix}-announcements` table, new IAM grants); no GSI operations on any existing table.
Expand Down
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
**An open-source, production-ready Generative AI platform for institutions**
*Built by Boise State University, designed for everyone.*

[![Release](https://img.shields.io/badge/Release-v1.18.0-6366f1?style=flat&logo=github&logoColor=white)](RELEASE_NOTES.md)
[![Release](https://img.shields.io/badge/Release-v1.19.0-6366f1?style=flat&logo=github&logoColor=white)](RELEASE_NOTES.md)
[![Nightly](https://github.com/Boise-State-Development/agentcore-public-stack/actions/workflows/nightly.yml/badge.svg)](https://github.com/Boise-State-Development/agentcore-public-stack/actions/workflows/nightly.yml)

![Python](https://img.shields.io/badge/Python-3.13+-3776AB?style=flat&logo=python&logoColor=white)
Expand Down Expand Up @@ -296,7 +296,7 @@ agentcore-public-stack/

See [RELEASE_NOTES.md](RELEASE_NOTES.md) for the full changelog, including new features, bug fixes, platform upgrades, and deployment notes for each release.

**Current release:** v1.18.0
**Current release:** v1.19.0

---

Expand Down
73 changes: 73 additions & 0 deletions RELEASE_NOTES.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,76 @@
# Release Notes — v1.19.0

**Release Date:** September 6, 2026
**Previous Release:** v1.18.0 (September 6, 2026)

---

> 🏗️ **CDK deploy required.** One new GSI — `UserArtifactsIndex` on the **existing** `{prefix}-user-artifacts` table. That is **one** index operation on one existing table, within DynamoDB's one-GSI-per-`UpdateTable` limit, so this release does not need splitting. Confirm the index reports `ACTIVE` before considering the deploy done — CloudFormation reporting `UPDATE_COMPLETE` is not the same thing.
>
> ⚙️ **`CDK_ARTIFACT_SHARE_INBOX_ENABLED` changes meaning.** It was opt-in (only `"true"` enabled the inbox); it is now a kill switch (unset or empty means **enabled**, only `"false"` disables). An environment that never set it **gains the "Shared with you" tab on this deploy**. Set it to `"false"` before deploying to keep that surface dark.
>
> 🧹 **Two optional one-shot scripts, both dry-run by default.** Neither is required for the deploy to be correct; see Deployment notes.

---

## Highlights

This release makes a conversation stop misreporting its own history. Two unrelated defects were doing it: a response that finished normally could be labelled **"Response interrupted"** with a Continue button that would resume an already-complete answer, and a response that genuinely *was* interrupted could show the model-directed `<interruption_note>` — text written for the model, not the user — inside the user's own chat bubble, permanently. Both are fixed where they originate rather than papered over at the render layer. Alongside them, the artifact **"Shared with you" inbox now ships on by default** with a kill switch, and `UserArtifactsIndex` is added to the artifacts table as groundwork with nothing reading it yet.

## Interrupted turns now tell the truth

Two independent bugs, both surfacing as a conversation describing itself incorrectly after an interruption.

**A completed turn could claim it was interrupted.** The SPA creates one `AbortController` per streaming request and used to release it only when the user clicked Stop — never on normal stream teardown. `streamingSessionIds()` reads exactly that controller to decide which turns a page departure interrupted, so after any turn that finished on its own, the session still looked in-flight for the rest of the tab's life. The next refresh, tab close, or cross-document navigation then attributed a `navigated_away` interruption to a finished answer, and a single departure marked *every* session that tab had ever streamed. On the next load the conversation showed "Response interrupted" and offered Continue, which would spend a turn resuming a response that had already ended.

**An interrupted turn could show the note meant for the model.** When a turn is interrupted, the next turn's prompt carries an `<interruption_note>` telling the model what happened. That note is deliberately part of persisted history — it is an honest record of what the model read — and the UI is supposed to render `displayText`, the clean copy of what the user typed, in its place. `displayText` was written by a single call site at the end of a *successful* turn, so a turn that was stopped, dropped, or errored never wrote one and the augmented prompt became the only text available to render. The failure concentrated where it was most visible: a turn only carries an interruption note because the *previous* turn was interrupted, so notes rode disproportionately on turns likely to be interrupted themselves.

### Backend

- `apis/app_api/sessions/routes.py` — `POST /sessions/{id}/interrupt` now verifies a turn is genuinely in flight against the session's single-flight lease before recording `navigated_away`. `user_stopped` is deliberately ungated: the Stop button only exists while streaming, and the same request arms distributed cancellation, which must still reach a turn whose lease read fails open.
- `agents/main_agent/session/hooks/display_text.py` — new `DisplayTextHook` writes `displayText` on `MessageAddedEvent`, when the user's message enters history and before the model call, so every later exit path already has it. Not every role-`user` message is the user: tool results carry that role, and Strands prepends a synthetic tool-result message ahead of the prompt when history ends on a dangling `toolUse` — precisely what an interrupted tool turn leaves behind — so content carrying `toolResult`/`toolUse` is skipped.
- `agents/main_agent/streaming/stream_coordinator.py` — arms the hook at the head of every turn, unconditionally including to `None`, because the agent instance is cached across turns. Its own end-of-turn write remains as a backstop for callers with no hook and for a failed write.

### Frontend

- `session/services/chat/chat-state.service.ts` and `chat-http.service.ts` — a session's controller is released on stream teardown, identity-checked so a superseded stream's late close cannot clear the controller of the stream that replaced it.

### Test Coverage

19 new backend tests across the hook and the coordinator's arming/backstop seam, plus SPA specs covering controller release, the identity guard, and the lease-gated and ungated interrupt paths.

## 🐛 Bug fixes

- **The sidebar said "No Chats Yet" while sessions were still loading**, showing an empty-state message to users who had conversations. The loading test read `value() === undefined`, but the sessions resource short-circuits to `null` on the ordinary cold-start path — `SessionService` is constructed during the `APP_INITIALIZER` pass, before the BFF bootstrap promise resolves — and `reload()` preserves that `null` through the real fetch, so the skeleton never rendered (#985)

## ⚠️ Changed

- **The artifact "Shared with you" inbox defaults on.** `ARTIFACT_SHARE_INBOX_ENABLED` was opt-in when the surface shipped in 1.18.0, because the surface landed before the product decision about it did. That decision has since been made and the inbox went live; leaving the default opt-in would mean every institution forking this repository silently loses a finished feature and has to discover a variable to get it back. `"false"` still turns it off (#986)

## 🏗️ Infrastructure

- **`UserArtifactsIndex`** on the existing `{prefix}-user-artifacts` table — `GSI2PK=USER#{uid}`, `GSI2SK=ARTIFACT#{updated_at}#{aid}`, sparse over HEAD rows. The artifact writer stamps both keys on both of its write paths. **Nothing reads the index in this release**; the library listing still serves from the base table, so the index is groundwork and can be deployed and backfilled without any user-visible change (#982)

## 🚀 Deployment notes

**A CDK deploy is required**, and one existing table gains one index. Verify it before moving on:

```bash
aws dynamodb describe-table --table-name <prefix>-user-artifacts \
--query 'Table.GlobalSecondaryIndexes[].{Name:IndexName,Status:IndexStatus}'
```

`UPDATE_COMPLETE` on the stack does not mean the index is usable — wait for `ACTIVE`.

**Check `CDK_ARTIFACT_SHARE_INBOX_ENABLED` before deploying.** Its default inverted: an environment that never set it gains the "Shared with you" tab. Set it to `"false"` first to keep that surface dark.

**Two optional one-shot scripts**, both dry-run unless given `--apply`:

- `backend/scripts/backfill_artifact_user_index_keys.py` — stamps `GSI2` keys onto artifact rows written before 2026-09-04. The index is sparse, so unstamped rows are absent from it rather than stale. Nothing reads the index yet, so this can run any time after it reports `ACTIVE` — but it must run before anything does.
- `backend/scripts/backfill_false_interrupted_markers.py` — clears interrupted-turn markers left by the defect above. It only touches `navigated_away` markers written more than 900s after the session's last message, which no interruption could have produced (the stream times out at 600s). Markers self-clear at the start of that session's next turn, so this only matters for conversations nobody returns to.

---

# Release Notes — v1.18.0

**Release Date:** September 6, 2026
Expand Down
2 changes: 1 addition & 1 deletion VERSION
Original file line number Diff line number Diff line change
@@ -1 +1 @@
1.18.0
1.19.0
2 changes: 1 addition & 1 deletion backend/pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"

[project]
name = "agentcore-stack"
version = "1.18.0"
version = "1.19.0"
requires-python = ">=3.10"
description = "Multi-agent conversational AI system with AWS Bedrock AgentCore"
readme = "README.md"
Expand Down
2 changes: 1 addition & 1 deletion backend/uv.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions frontend/ai.client/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion frontend/ai.client/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "ai.client",
"version": "1.18.0",
"version": "1.19.0",
"scripts": {
"ng": "ng",
"prestart": "tsx scripts/branding/generate-brand-theme.ts && tsx scripts/branding/generate-surface-theme.ts && tsx scripts/branding/generate-surface-colors.ts && tsx scripts/branding/generate-favicons.ts",
Expand Down
4 changes: 2 additions & 2 deletions infrastructure/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion infrastructure/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "infrastructure",
"version": "1.18.0",
"version": "1.19.0",
"bin": {
"infrastructure": "bin/infrastructure.js"
},
Expand Down
2 changes: 1 addition & 1 deletion tui/pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"

[project]
name = "agentcore-tui"
version = "1.18.0"
version = "1.19.0"
requires-python = ">=3.11"
description = "Terminal client for the AgentCore Public Stack — streaming AI chat in your terminal"
readme = "README.md"
Expand Down
2 changes: 1 addition & 1 deletion tui/src/agentcore_tui/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,6 @@

from __future__ import annotations

__version__ = "1.18.0"
__version__ = "1.19.0"

__all__ = ["__version__"]
2 changes: 1 addition & 1 deletion tui/uv.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.