Release/1.9.0#707
Merged
Merged
Conversation
…ness-q2-probe-result docs(kaizen): managed-Harness Q2 live-probe result — GO-with-boundary
…areable
Reframes the per-user markdown memory spec into the Memory Space primitive:
a named, first-class, bindable, templated, and shareable markdown wiki.
Oliver becomes a Chief-of-Staff template + a bound agent rather than a
special-cased feature.
- Adds the three-layer abstraction (Agent / Memory Space / declarative
binding) and the structural-config vs. semantic-MEMORY.md split.
- Space-keyed storage (SPACE#{id} + membership records + UserSpacesIndex
GSI) so sharing is expressible from day one.
- Entry types (entity / episodic / fact), Space Templates, and manifest-
indexed fields for relational/temporal queries ("who owes what").
- Sharing via owner/editor/viewer grants mirroring assistant collab-edit,
with run-as-user write attribution.
- Data governance is proportionate: same data class as sessions/artifacts
behind the same Entra JWT + RBAC — identity-based, no content-inspection
gate. Deletion-purge + inherited encryption are the only real items.
- 8-PR phasing; PR-1 (data layer) is the next buildout phase.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…paces-spec docs(memory): Memory Spaces — bindable, templated, shareable second-brain primitive (F5)
…rn-url-recovery docs(kaizen): recover resourceOauth2ReturnUrl section orphaned by #576 merge timing
Add a first-class "download the entire space as a .zip of raw markdown" capability (index + all entries, structure preserved, metadata.json), framed as the user-ownership / zero-lock-in property. Promotes the stubbed export endpoint into §9 with contents, access, streaming mechanics, and an import-friendly round-trip note; folds the zip export into PR-5. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…pace-export-zip docs(memory): full-ownership zip export of a Memory Space
Adds the F5 Memory Space primitive's data layer — no runtime wiring, gated by MEMORY_SPACES_ENABLED (default off). Backend (apis/shared/memory/): - store.py: S3 content-addressed byte store (sibling of the skills store) - models.py: MemorySpace / MemoryIndex / MemoryEntryRef / SpaceMember - templates.py: Blank / Chief-of-Staff / Research-Notebook presets - repository.py: dedicated memory-spaces table CRUD (META/INDEX/MEMBER rows, OwnerIndex + MemberIndex GSIs, Decimal handling) - service.py: permission-gated lifecycle + sharing + entry/index I/O, resolve_permission chokepoint (viewer reads, editor writes, owner shares/deletes), content-addressed writes with GC-on-replace - feature_flags.py: memory_spaces_enabled() (default off) - 47 moto-backed tests; import boundaries clean Infrastructure: - MemorySpacesConstruct: S3 bucket + dedicated memory-spaces DynamoDB table (OwnerIndex/MemberIndex GSIs), a per-domain table matching the project's actual pattern - Threaded via PlatformComputeRefs to both compute roles (readwrite S3 + DynamoDB); env vars S3_MEMORY_SPACES_BUCKET_NAME / DYNAMODB_MEMORY_SPACES_TABLE_NAME / MEMORY_SPACES_ENABLED - config.ts flag default-off; resource-count assertions updated - tsc + 429 jest tests pass Spec updated to reflect the dedicated-table + two-GSI decisions. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…paces-pr1-data-layer feat(memory): Memory Spaces data layer (PR-1)
…orkstreams Splits the plan into two workstreams to keep the Memory Space a clean bindable primitive: Workstream A (this epic) delivers the primitive + the user-facing "own your data" surface (data layer, app-api CRUD, export, sharing, SPA panel, consolidation); Workstream B (Agent/Harness layer) delivers agent-consumption — the memory_* tools, declarative binding, and system-prompt index injection — so any run surface can bind the same primitive rather than welding it to inference-api. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…eslice-phasing docs(memory): re-slice phasing — primitive vs. agent-consumption workstreams
Workstream A2 of the re-sliced memory epic: the user-facing "own your data"
surface over the Memory Space primitive. No agent-consumption (tools /
binding / prompt injection) — that's the Agent/Harness workstream.
app-api (apis/app_api/memory_spaces/):
- routes.py: /memory/spaces CRUD over MemorySpaceService — list (with
templates + accurate per-space role), create-from-template, get (index +
entry manifest), delete-or-leave, entry read/list/upsert/delete, index
read/update. Sync handlers (FastAPI threadpools the sync boto3 service).
- Gated by require_memory_spaces_user: 404 while MEMORY_SPACES_ENABLED off
(surface behaves as unmounted); cookie auth via get_current_user_from_session.
- Service errors translated NotFound->404, Permission->403, Error->400.
- models.py: camelCase request/response models.
- Mounted before the existing /memory (AgentCore Memory) router; paths are
non-overlapping (/memory/spaces vs /memory/{record_id}).
shared service:
- leave_space(): a member drops their own grant (the shared-in forget-me
case); owner cannot leave.
- list_spaces_for_user() now returns (space, role) so shared-in spaces carry
the member's real viewer/editor grant (only consumer is the new route).
Tests: 12 route tests (moto-backed real service, flag gate, CRUD, 403/404,
member-leaves-via-delete) + leave_space service tests. Full memory suite +
import boundaries green (69 passing).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…2-app-api-surface feat(memory): Memory Spaces user surface — /memory/spaces CRUD (A2)
The "own your data" leg of Workstream A: a loss-free `.zip` download of a
space's raw markdown (§9). `MemorySpaceService.export_space` gathers the
corpus once (index + every entry's bytes) behind the viewer+ permission
gate, including the member grant list only for editor+ callers (mirrors
`list_members`). The app-api route builds the archive in a
`SpooledTemporaryFile` — spilling to disk beyond 8 MiB so a large space
never pins memory — and streams it back.
Zip mirrors the S3 layout (`{name}/MEMORY.md`, `entries/<type>/<slug>.md`,
`metadata.json`) so it is self-contained and re-importable later. Archive
path components are sanitized against zip-slip. Route tests cover layout,
verbatim frontmatter, owner-vs-viewer member disclosure, 403/404/flag-off,
and the hostile-slug case.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…3-export-zip
feat(memory): Memory Space zip export — /memory/spaces/{id}/export (A3)
…(A4)
The sharing leg of Workstream A, plus the concurrency guarantee that makes
multi-editor spaces safe.
Sharing surface (app-api, over the existing service grant methods):
- GET /memory/spaces/{id}/shares list grants (editor+)
- POST /memory/spaces/{id}/shares grant viewer|editor (owner)
- PATCH /memory/spaces/{id}/shares/{email} change a grant's role (owner)
- DELETE /memory/spaces/{id}/shares/{email} revoke (owner, idempotent)
New `MemorySpaceService.update_share` gives PATCH proper not-found semantics
and preserves the grant's original createdAt (distinct from share's upsert).
Optimistic manifest concurrency (the real design content):
- `MemorySpaceRepository.put_index(expected_version=…)` does a conditional
DynamoDB write on the manifest `version`, raising the repository-local
`OptimisticLockError` on a mismatch.
- `write_entry`/`delete_entry` route through a new `_mutate_index` helper: a
bounded read-modify-conditional-write retry loop. Because an entry write
touches a single slug, re-reading the fresh manifest and re-applying is safe;
it converges on transient races and raises `MemorySpaceConcurrencyError`
(→ 409) only on a sustained one. Behavior is unchanged for single-writer
spaces.
Tests: 6 route tests (share CRUD, member gains access, non-owner 403, viewer
can't list, PATCH-unknown 404, owner-role 422) + 7 service tests (update_share
role/origin/owner-gate + version-increments, stale-write rejected, retry
converges, gives-up-after-max). 76 memory + import-boundary tests green.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…4-sharing feat(memory): Memory Space sharing + optimistic manifest concurrency (A4)
…rt (A5) The user-facing "Memory" surface for the Memory Space primitive, under frontend/ai.client/src/app/memory-spaces/. Makes A2–A4 visible to users. - List page: owned + shared-in spaces as cards with role/template badges; per-card open, share (owner), download .zip, delete/leave. Empty, loading, error, and feature-unavailable states. - Detail page: view/edit the MEMORY.md index (editor+) and the entry list; entries open in a dialog to view (viewer) or edit/create (editor+); delete per entry. Header carries share/download/delete-or-leave. - Create-from-template dialog and a share dialog (add-by-email + per-row role + delta-on-save over the A4 /shares endpoints). Viewer access is read-only throughout; the share dialog fails soft for non-owners. - Signal facade (MemorySpaceService) + thin API service mirror the assistants/schedules pattern. The nav entry rides a live accessible$ probe: a 404 (MEMORY_SPACES_ENABLED off) hides it, matching showSchedules. - Routes memory-spaces + memory-spaces/:id (authGuard); redesign-tokens and @angular/cdk/dialog conventions throughout. Facade spec (7 tests) green; dev build + tsc clean; sidenav specs still pass. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…rejection) The A5 sidenav now injects MemorySpaceService and probes `loadSpaces()` in the auth effect. The sidenav spec mocked ScheduleService but not the new service, so the authenticated-probe test constructed the real MemorySpaceService, which fired a real XHR to /memory/spaces (status 0, no backend); `loadSpaces` then re-threw, surfacing as a Vitest "unhandled rejection" at suite level. Mock MemorySpaceService exactly as ScheduleService is mocked, and add matching coverage: probe fires once authenticated (not while unauthenticated) and the `showMemorySpaces` nav gate resolves null→false, false→false, true→true. Full suite: 1422 passed, 0 errors. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…5-spa-panel feat(memory): Memory Spaces SPA panel — list/detail/create/share/export (A5)
app-api owns the Memory Spaces CRUD surface (`/memory/spaces/*`) but its container environment only set MEMORY_SPACES_ENABLED — never the table or bucket names the service reads (DYNAMODB_MEMORY_SPACES_TABLE_NAME / S3_MEMORY_SPACES_BUCKET_NAME). Without them the repository falls back to the default "memory-spaces" table name, which doesn't exist, so every read throws a boto3 ResourceNotFoundException that the centralized handler maps to a 502 "Upstream service error." (inference-api already sets the identical trio, but per the service-boundary rule it isn't the one serving these routes.) Thread `refs.memorySpacesTable`/`.memorySpacesBucket` through AppApiSsmParams and emit both names next to the flag in buildAppApiEnvironment. Names are always wired (read lazily); only MEMORY_SPACES_ENABLED gates route mounting, so flipping the switch on later needs no env change. New unit test guards the wiring; tsc + 431 infra jest tests green. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Fold the lesson from the app-api env-wiring fix into the cdk-infrastructure skill so the next construct-author sees it while wiring, not after a 502. Adds a "Cross-Construct References" subsection: set a resource's name env var on every compute that reads it (one doesn't imply the other), the silent-502 failure mode (default-name fallback → ResourceNotFoundException → generic 502, invisible to synth/CI), and the env-map test guard + service-boundary caveat. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…s-app-api-env-wiring fix(memory): wire memory-spaces table/bucket names onto app-api
The safe, non-LLM slice of Workstream A6. `MemorySpaceService.consolidate`
(editor+) + `POST /memory/spaces/{id}/consolidate` → a `ConsolidationReport`.
Auto-fixes only storage hygiene: orphaned content-addressed objects — keys
under a space's prefix that no manifest entry or the index pointer references
(leaks from crashed/raced writes) — are GC'd (new `MemorySpaceStore.list_keys`
drives it). Everything that needs a judgment call is *reported, not mutated*:
- duplicate content across slugs (same content hash) — which slug survives is
semantic, so it's flagged, never auto-merged;
- dead `[[slug]]` wikilinks in MEMORY.md — reported; opt-in `stripDeadLinks`
unlinks them (they point nowhere) while preserving the surrounding prose;
- over-cap entry counts (`MEMORY_SPACE_INDEX_CAP`, default 200) — flagged, never
auto-evicted.
This deliberately does not merge/evict/rewrite durable memory — that's deferred
to the LLM consolidation pass (Workstream B era), which extends this exact
`consolidate()` seam once agentic writes create real duplication/staleness to
act on. On-demand only for now; scheduler/threshold auto-run and SPA surfacing
are follow-ups.
Tests: 8 service (healthy report, orphan GC + skip, dup-report-no-merge,
dead-link report + strip-keeps-prose, over-cap flag, editor gate) + 4 route
(report shape, no-body, viewer 403, flag-off 404) + 3 store (list_keys prefix
scoping / empty / disabled). 104 memory + boundary tests green.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…6-consolidation feat(memory): deterministic consolidation health pass (A6)
Captures the strategy for the "Agent Designer" (Agent Harness Editor): a new authoring surface that composes an Agent from RBAC-governed primitives (instructions, model, KBs, tools, skills, Memory Spaces, + future), replacing the term/feature "Assistant." Locks the load-bearing decisions: own a primitive-agnostic Agent contract and federate AgentCore Registry later rather than build on it (adopt-with-boundary precedent); evolve the assistant store in place (no parallel table); a uniform bindings[] model with the model as a governed single-select; RBAC = compose the five existing per-primitive access checks (incl. ModelAccessService), not a new system; design-time filter + run-time re-resolution per invoker with block-on- missing v1; ship memory-consumption as a thin vertical slice before the full Designer. Phasing 0–5 + later AWS federation; supersedes the memory spec's "extend the Assistant" §B1 framing. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…signer-spec docs(agent): Agent Designer spec — unified primitive-binding surface
…dels Phase 1 (PR-1) of the Agent Designer. Pure library, zero behavior change: legacy Assistants read unchanged and no caller passes the new fields yet. - AgentModelConfig (D3 governed single-select; field is model_settings/ alias modelConfig to dodge pydantic's reserved model_config — R3) - AgentBinding (open kind on read, KNOWN_BINDING_KINDS for request validation) - optional model_settings + bindings on Assistant (additive) - compat.effective_bindings/to_agent_view (D2): absent bindings synthesize a knowledge_base binding reffing the assistant id (KB's only stable identity, F4 deferred — R4); absent model maps to None, never fabricated (R1) - Decimal-safe serialization for modelConfig.params floats Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Phase 1 (PR-2). The Agent fields now round-trip through the rag-assistants store and are validated at write time by composing existing RBAC checks (D4). Legacy clients are unaffected: the SPA sends none of the new fields and the AssistantResponse surface is unchanged. - service.create_assistant/update_assistant thread bindings + model_settings; to_ddb_safe on write / from_ddb on read so modelConfig.params floats survive DynamoDB (Decimal); explicit [] replaces bindings, absent leaves them untouched - app_api/agents/services/binding_validation.py composes model access (ModelAccessService), memory resolve_permission (viewer+/editor+), the implicit-KB rejection, and inert shape-only checks for tool/skill (D4/D5) - assistants POST/PUT validate then pass through; validation raises 4xx outside the create handler's generic except so it isn't masked as 500 - tests: validation matrix (incl. inert no-RBAC guarantee), persistence round trip, legacy no-field read Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…signer-phase1 feat(agents): Agent Designer Phase 1 — contract + binding persistence (foundation)
…nto-develop-1.8.0 Backmerge: main into develop (1.8.0)
Three defects in the chat input's auto-sizing: - The textarea carried `overflow-hidden`, so once content exceeded the visible area there was no way to scroll within it. - `onTextareaInput` set `height = scrollHeight` with no clamp. Past 200px the inline height kept growing while `max-height` capped the rendered height, leaving the two diverged and the scrollbar unreachable. The growth is now clamped in a shared `autoResize()`. - `submitChatRequest` cleared the value but left the stale inline height, so the input stayed expanded after sending. Adds `resetTextareaHeight()`. Also drops the `.chat-textarea` CSS block: it declared its own min-height/max-height/field-sizing but the class is applied nowhere in the template, so the rules were dead while contradicting the real inline styles. The JS path is now the sole sizing authority. The `isExpanded` signal goes too — it was written on submit and never read. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…k prompt-cache hits Tool-content truncation previously ran on every session restore behind a sliding protected-turns window, so each new turn re-mutated the turn that just aged past the window. Bedrock prompt caching requires an exact prefix match, so this forced a full prefix cache re-write (~$2.5/MTok on a 35k-150k prefix) nearly every turn — costing far more than the read tokens truncation saved (evidence: prod session aecd387d, inter-turn prefix shrinkages of -382/-1035/-1513 with cacheRead=0 inside the cache TTL). Redesign: truncation is now driven only by a persisted truncation_anchor in the compaction state (sessions-metadata `compaction` attribute): - The anchor moves when the checkpoint advances (update_after_turn), where the slice already pays the single cache re-write — one mutation per compaction event instead of one per turn. - It also advances opportunistically at restore when more than cache_ttl_seconds (default 300s, AGENTCORE_MEMORY_COMPACTION_CACHE_TTL_SECONDS) have passed since the previous turn: the cache entry has expired anyway, so pending truncations are applied for free. - Legacy state records without the field default the anchor to the checkpoint, so retained history stops being mutated immediately on upgrade. - The compaction-failure path in initialize() now resets _compaction_state_loaded so update_after_turn re-loads persisted state instead of overwriting checkpoint/anchor with defaults. Removes the now-dead _find_protected_indices sliding-window helper and adds tests/agents/main_agent/session/test_compaction_stability.py asserting agent.messages is byte-identical across consecutive restores whenever no compaction-state change occurs. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…cache Skill records reached the AgentSkills <available_skills> system-prompt block in nondeterministic order, changing the prompt between turns of the same session and invalidating the Bedrock prompt cache (exact-prefix match) — forcing full cache re-writes on turns well inside the TTL. Two sources, fixed at three layers: - batch_get_skills returned raw DynamoDB batch_get_item response order; now sorted by skill_id. - resolve_user_permissions built grant unions as sets and returned list(set) — iteration order varies per process via hash randomization; tools/models/skills now sorted. - build_skills_runtime sorts records before constructing AgentSkills as defense in depth at the injection point. Regression tests force a descending batch_get_item response order and a reversed fetch order, both verified to fail without the fix. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…ad the stable prefix CacheConfig(strategy="auto") places exactly one message-level cachePoint; when its lookup misses, nothing is read and the whole prefix re-writes at the cache-write premium. One proven miss mode is structural: Anthropic's cache lookback checks only ~20 content blocks behind the breakpoint, so a wide parallel tool fan-out (18 parallel calls = ~38 new blocks) pushes the previous checkpoint out of range — prod session aecd387d observed cacheRead=0 / cacheWrite=134k mid-turn (~$0.34). Now the request carries 3 of Bedrock's max-4 cachePoints: - toolConfig tail via cache_tools="default" - system tail via SystemContentBlock list with trailing cachePoint (built in AgentFactory; the cache_prompt config key is deprecated) - last-user-message point via the existing auto strategy (which strips only message-level points, never system/tools ones) Both new points are gated on ModelConfig.bedrock_cache_points_supported() (mirrors Strands' _cache_strategy predicate) because unlike auto mode they would be sent verbatim to non-Anthropic models and rejected. Verified live on global.anthropic.claude-sonnet-5 via ConverseStream: simulated message-level miss reads the 6.5k-token system+tools prefix from cache (cacheRead=6497, cacheWrite=83) instead of re-writing it. CountTokens accepts the cachePoint-bearing request, so context attribution is unaffected. Position/budget test asserts exactly 3 cachePoints. Upstream: strands-agents/harness-sdk#3348 proposes auto mode keep a rolling pair of message cachePoints so fan-outs stay within the lookback. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
… call
A $1.60 prod conversation audit (session aecd387d) showed 75% of spend was
avoidable Bedrock prompt-cache re-writes, and diagnosing the causes took
hours of manual forensics against raw DynamoDB rows. This makes the whole
class measurable end to end:
- PrefixFingerprintHook (BeforeModelCallEvent) hashes the three cacheable
prefix components per model call — toolConfig (order-sensitive canonical
JSON), effective system prompt (captured after AgentSkills injection),
and message history excluding the newest message. The stream coordinator
persists entry N on the turn's Nth assistant-message cost row, so a miss
is diagnosable with a column diff instead of row forensics.
- Write-time cacheStatus per cost row (first_write | hit | miss_ttl_expired
| miss_avoidable | uncached) derived from the session's previous C# row
(one GSI read), plus wastedUsd for avoidable misses priced at the
cache-write premium over cache-read from the row's own pricingSnapshot.
Turn rows now write sequentially (was parallel) so each call classifies
against its true predecessor.
- Session-row rollups next to totalCost: totalCacheReadTokens,
totalCacheWriteTokens, avoidableMissCount, wastedUsd — cache-efficiency
ratio for lists/admin without scanning cost rows.
- Admin cost anatomy: GET /admin/costs/sessions/{sessionId}/calls
(require_admin) returns chronological per-call rows with token splits,
cost, cacheStatus, and fingerprints, plus session-level cache summary.
- CloudWatch EMF per call (CacheReadTokens / CacheWriteTokens /
AvoidableMiss / WastedUsd) via a raw-JSON stdout logger — dashboard and
alarm on fleet cache-write share with no SDK calls or extra IAM.
- CI determinism guard: builds the chat-agent surface twice from shuffled
skill/role/tool record orders (RBAC merge -> skills runtime -> Agent ->
AgentSkills injection) and asserts identical system-prompt and toolConfig
fingerprints; verified to fail when the skill-ordering sort is removed.
Complements the sorting + byte-stability fixes from
feature/prompt-cache-stability (merged in).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
PROMPT_CACHE_OBSERVABILITY_ENABLED=false disables the fingerprint hook, per-call cacheStatus derivation (and its GSI read), and EMF emission — default ON per house convention; empty string stays enabled. Raw cacheRead/cacheWrite token rollups are unaffected (usage passthrough, not derived). CLAUDE.md now encodes the determinism + byte-stability contract and the fingerprint-based debugging recipe. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…ache-stability fix: preserve Bedrock prompt-cache hits across turns (deterministic ordering + byte-stable compaction)
…extarea-sizing fix(chat-input): make the textarea scrollable and reset it after submit
- CLAUDE.md: add "token cost effectiveness is a design tenet" bullet under Key Conventions — prefix determinism / bounded per-turn payloads / verify via the #697 observability, balanced so quality wins on genuine conflict. - kaizen review-queue: queue the two deferred #697 follow-ups — track harness-sdk#3348 (rolling-pair cachePoints; local workaround gated on dashboard evidence) and the ContextOffloader S3 adoption spike (with its four known gotchas). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
follow-up) New cross-service construct area lib/constructs/observability/ with a PromptCacheObservabilityConstruct composed into PlatformStack. Graphs the dimension-less EMF metrics both APIs emit into AgentCoreStack/PromptCache (cache read/write tokens, a cache-efficiency MathExpression, AvoidableMiss, WastedUsd) plus a Logs Insights widget over the runtime log group grouped by cacheStatus. Console-only alarms on AvoidableMiss and WastedUsd Sums (stricter in prod, NOT_BREACHING on missing data so the PROMPT_CACHE_OBSERVABILITY_ENABLED kill switch stays quiet). No SNS — alerting infra is deliberately out of scope, matching kb-sync and scheduled-runs. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Consumes GET /admin/costs/sessions/{id}/calls (backend PR #697):
- SessionCostAnatomy / SessionCallRow / PrefixFingerprints models + CacheStatus union
- AdminCostHttpService.getSessionCostAnatomy with URL-encoded session id
- New /admin/costs/sessions/:id route (lazy, component input binding)
- Drill-down page: summary rollups (total cost, cache efficiency incl. null,
avoidable misses, wasted USD, cache read/write tokens), chronological calls
table with color-coded cacheStatus badges, and prefix-fingerprint diffing
that flags which hash (tools/system/history) flipped vs the previous
fingerprinted call — the cache-buster diagnosis on miss_avoidable rows.
Expandable rows show full hashes + messageCount; 404 renders a
no-cost-rows empty state.
- Session-id lookup form on the Cost Analytics dashboard as the entry point
- Vitest specs for the diff util, HTTP method, and page states
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…he-followup-breadcrumbs chore: PR #697 follow-up breadcrumbs + token-cost-effectiveness tenet
…ache-dashboard feat(observability): prompt-cache CloudWatch dashboard + alarms
…st-anatomy-page feat(admin-costs): per-session cost-anatomy drill-down page
…d calls as miss_avoidable When every prior call in a session was uncached (prompt below the model's minimum cacheable prefix, e.g. ~4096 tokens on Claude Haiku 4.5), the first call that crosses the threshold does cacheWrite>0/cacheRead=0 and was classified miss_avoidable — inflating the AvoidableMiss and WastedUsd EMF metrics and the admin Session Cost Anatomy page. Verified live in dev-ai session 9a1f25b2 (calls 1-5 uncached at 3.5-4k tokens, call 6 falsely flagged with write=4122/read=0). classify_cache_status now takes the previous call's cached-prefix token total: when the immediately preceding call had zero cache activity there was no entry to read from, so the write is classified first_write (the expected initial population) and excluded from waste pricing. Unknown (None) keeps the previous behavior. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…voidable-below-threshold fix(observability): first cache write after below-threshold calls is first_write, not miss_avoidable
The AgentCore Runtime env block set DYNAMODB_USER_FILES_TABLE_NAME but not S3_USER_FILES_BUCKET_NAME, so the Word-document tools' _user_files_bucket() fell back to the literal 'user-files' default and PutObject failed with AccessDenied (and earlier PermanentRedirect against that unrelated bucket). The runtime role's UserFilesBucketAccess already grants Get/Put/Delete/List on the real bucket; this just points the runtime at it. tsc build passes.
…er-files-bucket-env Set S3_USER_FILES_BUCKET_NAME on inference-api runtime
_user_files_bucket() previously defaulted to a literal 'user-files' bucket when S3_USER_FILES_BUCKET_NAME was unset, which surfaced a missing runtime env var as a confusing S3 PermanentRedirect/AccessDenied. It now raises _StorageNotConfiguredError, and create/modify/read short-circuit before the Code Interpreter run with a clear 'storage is not configured' message.
…storage-config Fail loudly when Word doc storage bucket is unconfigured
Feature release making Bedrock prompt-cache economics stable and measurable. Fixes three cache-busting defects in the model-call path (per-turn history mutation via the sliding truncation window, nondeterministic skill ordering, single fragile cachePoint) and adds end-to-end observability: prefix fingerprints + cacheStatus classification on every cost row, the admin Session Cost Anatomy page, per-call EMF metrics, and a CloudWatch dashboard with alarms. Also fixes chat-input textarea sizing and points the runtime's Word-document tools at the real user-files bucket. - Prompt-cache observability layer with PROMPT_CACHE_OBSERVABILITY_ENABLED kill switch (#697) - Admin Session Cost Anatomy drill-down at /admin/costs/sessions/:id (#700) - Byte-stable restored history, deterministic skill ordering, 3 cachePoints (#697) - first_write vs miss_avoidable classification fix after below-threshold calls (#701) - PromptCacheObservabilityConstruct dashboard + alarms (#699) - S3_USER_FILES_BUCKET_NAME on the inference-api Runtime + fail-loud storage guard (#702, #706) - Chat input textarea scroll/clamp/reset fixes (#696) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
| import { NgIcon, provideIcons } from '@ng-icons/core'; | ||
| import { heroArrowLeft, heroChevronDown } from '@ng-icons/heroicons/outline'; | ||
| import { AdminCostHttpService } from '../services/admin-cost-http.service'; | ||
| import { CacheStatus, SessionCallRow } from '../models'; |
| @@ -0,0 +1,160 @@ | |||
| import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest'; | |||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
v1.9.0 — Prompt-cache stability + observability
Feature release making Bedrock prompt-cache economics stable and measurable.
Prompt-cache stability (#697) — three cache-busting defects fixed in the model-call path:
truncation_anchorreplaces the sliding truncation window that re-mutated history — and re-wrote the whole cached prefix — nearly every turn)Prompt-cache observability (#697, #699, #700, #701):
cacheStatus(first_write | hit | miss_ttl_expired | miss_avoidable | uncached) +wastedUsdon every cost row; session-level cache rollups;GET /admin/costs/sessions/{id}/calls; per-call EMF metrics;PROMPT_CACHE_OBSERVABILITY_ENABLEDkill switch (default ON)/admin/costs/sessions/:idwith fingerprint diffing that names the cache-busterPromptCacheObservabilityConstruct: CloudWatch dashboard + console-only alarms overAgentCoreStack/PromptCachefirst_writeno longer misclassified asmiss_avoidableafter below-threshold callsFixes:
S3_USER_FILES_BUCKET_NAMEset on the inference-api Runtime so Word-document saves stop failing withAccessDenied; tools now fail loudly if the bucket is unconfigured (Set S3_USER_FILES_BUCKET_NAME on inference-api runtime #702, Fail loudly when Word doc storage bucket is unconfigured #706)Deploy:
platform.yml(CDK required — dashboard construct + Runtime env var) →backend.yml→frontend-deploy.yml. No backfills, no data migration, no dependency changes.Squash-merge per release convention; backmerge PR into
developfollows.🤖 Generated with Claude Code