Release/1.10.0 - #715
Merged
Merged
Conversation
…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)
Phase 1 (PR-3). A governed Agent read/write surface over the evolved assistant store: same shared service functions and identity-based access gates as /assistants, but returning the Agent shape (compat.to_agent_view -> AgentResponse) so callers see modelConfig + bindings. Legacy ids valid unchanged. - feature_flags.agents_enabled(): AGENTS_API_ENABLED, default OFF (memory-spaces pattern) — surface 404s while off, ships incrementally, /assistants unaffected - app_api/agents/routes.py: require_agents_enabled 404-gate; draft/create/list/ get/update/delete + 4 shares endpoints, delegating to apis.shared.assistants service and reprojecting via to_agent_view; create/update run binding_validation - AgentResponse/AgentsListResponse/AgentSharesResponse (agentId == assistantId) - main.py mounts the router - test-chat + document sub-routes deliberately excluded (would force a 2nd architecture import-boundary exception); list is owner+shared (public/pagination parity deferred to the Phase-4 Designer) - tests: 404-gate, agentId/bindings projection, CRUD permission gating, shares Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…signer-agents-router feat(agents): Agent Designer Phase 1 — /agents surface (dark)
Phase 1 (PR-4). Completes Phase 1 deployability: the /agents surface can now
be turned on per environment. No new AWS resources — the flag only gates
whether the routes 404 (the assistant store it reads is always present).
- config.ts: AgentsConfig { enabled }; CDK_AGENTS_API_ENABLED (default off,
empty-string-safe) or an `agents.enabled` cdk.json context, mirroring
memorySpaces exactly
- app-api-environment.ts: AGENTS_API_ENABLED env on app-api
- infra tests: default-off / opt-on assertion; mock-config default
- docs: agent-designer.md Phase-1 status (+ the two refinements and the
Oliver-dogfood-gated-on-Phase-3 note); CHANGELOG [Unreleased]
The live Oliver dogfood (D6) is deliberately NOT included: it needs Phase 3
harness resolution + Memory Spaces deployed to the target env before a
memory_space binding resolves at invocation. Tracked as the Phase 3 payoff.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…signer-flag-plumbing feat(agents): Agent Designer Phase 1 — AGENTS_API_ENABLED flag plumbing + docs
…se 3 PR-0) Phase 3 harness resolution runs inside inference-api, so the runtime needs the same flag the app-api surface got in #593. Default off, mirrors the app-api wiring; without it the harness ignores Agent bindings entirely (today's behavior). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…hase 3 PR-A) The Harness now re-resolves an Agent's governed modelConfig against the INVOKING user (D5) and applies it to model selection. Absent modelConfig ⇒ the model resolves exactly as today; gated on AGENTS_API_ENABLED (off in all envs still). - agent_binding_resolver.py: resolve_agent_invocation() checks the pinned model against AppRoleService.can_access_model for the invoker (R2 — same gate the harness uses elsewhere), returns a model_override or raises AgentBindingBlockedError. inference-api imports apis.shared only (boundary-safe) - routes.py /invocations: resolve after assistant load, before the KB search; on block, stream a conversational stream_error via stream_conversational_message (D5 block-with-message, no silent downgrade). Override wins at model resolution; agent params sit beneath request params, still flowing through admin bounds/locks - tests: allowed→override, denied→block (checked vs invoker), no-modelConfig→ empty plan (no RBAC call); 57 existing inference/chat tests unchanged Co-Authored-By: Claude Opus 4.8 <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
…nto-develop-1.9.0 Backmerge main into develop after 1.9.0
- Add excel_spreadsheet_tool.py with create/modify/list/read tools for .xlsx files - Create office/_storage.py module with shared Code Interpreter and S3 storage utilities for Word and Excel - Implement Excel toolset integration in inference_api chat routes with tool injection - Add "Excel Spreadsheets" catalog entry to DEFAULT_TOOLS in seed_bootstrap_data.py - Replace word-document-renderer with generic file-download-renderer for all generated office documents - Extend Word document tool to use shared office storage module - Update test fixtures and chat routes to support Excel tool provisioning - Generated Excel files are persisted to S3_USER_FILES_BUCKET_NAME and appear in chat Files panel
…dsheet-tools feat(office-tools): add Excel spreadsheet toolset
The SPA distribution's CloudFront ResponseHeadersPolicy only ever opened
frame-src to 'self' and the artifacts origin, so every domained deploy
CSP-blocked the MCP App sandbox iframe (mcp-sandbox.{domain}) — the
sandbox side's frame-ancestors was locked to the SPA origin from the
start, but the SPA side was never extended. Localhost dev bypasses
CloudFront's response headers entirely, which masked the gap through
live verification.
Thread the already-computed mcpSandboxProxyOrigin from PlatformStack
into SpaDistributionConstruct and append it to frame-src. Add a
regression test that synths a domained PlatformStack and asserts both
iframe origins are present in the frontend headers policy.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…c-mcp-sandbox fix(infra): allow the mcp-sandbox origin in the SPA frame-src CSP
Feature release adding Excel spreadsheet creation and editing to chat, plus the CSP fix that unblocks MCP App iframes on deployed environments. - Excel spreadsheet toolset (create/modify/list/read .xlsx via openpyxl in Code Interpreter) behind a single "Excel Spreadsheets" catalog toggle; files land in the chat Files panel (#709) - Shared office/_storage.py module for Word + Excel; generic file-download renderer replaces the Word-specific one (#709) - SPA CloudFront CSP frame-src now includes the mcp-sandbox origin, fixing MCP App UIs blocked on every domained deploy (#714) - CDK deploy required: SPA ResponseHeadersPolicy update via platform.yml Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
| return {"content": [{"text": text}], "status": "error"} | ||
|
|
||
|
|
||
| _NO_CI_MESSAGE = ( |
| return {"content": [{"text": text}], "status": "error"} | ||
|
|
||
|
|
||
| _NO_CI_MESSAGE = ( |
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.10.0
Feature release adding Excel spreadsheets to chat and fixing the CSP gap that blocked every MCP App iframe on deployed environments. Full narrative in RELEASE_NOTES.md; factual log in CHANGELOG.md.
What ships
create/modify/list/read_excel_spreadsheetbuild and edit.xlsxvia openpyxl in the sandboxed Code Interpreter; files persist to the user-files bucket and appear in the chat Files panel. One catalog toggle ("Excel Spreadsheets", gate keycreate_excel_spreadsheet, off by default) provisions the set. Word tools refactored onto the new sharedoffice/_storage.py; generic file-download renderer replaces the Word-specific one.frame-srcnever included themcp-sandbox.{domain}origin, so domained deploys CSP-blocked every MCP App iframe (localhost bypasses CloudFront headers, masking it).PlatformStacknow threads the sandbox proxy origin intoSpaDistributionConstruct, with a synth-time regression test.Version bump
Minor (
1.9.0→1.10.0) — new user-facing capability.VERSION+ all manifests synced (sync-version.sh --checkpasses), lockfiles regenerated.Deployment
platform.ymlrequired — SPA CloudFrontResponseHeadersPolicyupdate (CSP change). Thenbackend.yml→frontend-deploy.ymlas usual.After merge
Squash-merge, then backmerge
main→develop(merge commit, not squash).🤖 Generated with Claude Code