Release/1.7.1#675
Merged
Merged
Conversation
…-and-user-memory-specs docs: add tool-search token-bloat + per-user markdown memory specs
A reauth pause was opaque — "credentials need re-consent" gave no hint why. The vault token is keyed by (workload identity, userId), so the overwhelmingly common cause is that the token was vaulted under a DIFFERENT workload identity than the worker queries: e.g. a consent done through local dev (AGENTCORE_RUNTIME_WORKLOAD_NAME=local_dev_inference) can't be read by the deployed worker (platform-workload), and vice versa. That exact mismatch cost hours to diagnose. Surface the workload name, userId, and provider the lookup used in the pause warning so the mismatch is obvious in one log line. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…reauth-diagnostics feat(kb-sync): log workload identity + userId on a reauth pause
KB sync ships dark: the EventBridge rule is created disabled and the
KB_SYNC_ENABLED kill switch is false unless CDK synths with
CDK_KB_SYNC_ENABLED=true (config.ts). platform.yml never forwarded that
var, so a CDK deploy always synthed the feature off — the only way to
enable it was an out-of-band CLI flip that reverts on the next deploy.
Forward it as an environment-scoped `${{ vars.CDK_KB_SYNC_ENABLED }}`
like every other CDK_ var. The development environment variable is set
to "true" (enables the rule + kill switch in dev-ai durably); production
leaves it unset, so it stays dark until validated there.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…able-dev chore(kb-sync): plumb CDK_KB_SYNC_ENABLED into the platform deploy
KB sync shipped opt-in (default off), a holdover from ship-dark
incremental development. The feature is complete and guarded, so a
deployer/cloner of the public stack should get it working by default —
a hidden default-off flag is bad DX. Invert to opt-out: enabled unless
CDK_KB_SYNC_ENABLED=false (or a `kbSync.enabled: false` cdk.json context).
The workflow forwards `${{ vars.CDK_KB_SYNC_ENABLED }}`, which is an empty
string when unset — a plain `?? false` fallback wouldn't flip the default,
so config.ts treats empty/unset as "use the default (on)" and only the
literal "false" as the kill switch. Adds config-resolution tests covering
unset / empty / "true" / "false" / context-disable.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…fault-on chore(kb-sync): default the feature ON with a kill switch
The knowledge-base sync control was a bare "Manual only" dropdown that
never explained what syncing does, and its status line only appeared
while a schedule was active — a manual-only file showed nothing.
- Lead the control with an "Auto-sync from <source>" label (with a
descriptive title tooltip) so every row states what it is and where it
pulls from.
- Rename the options to self-describing verbs: "Don't auto-sync",
"Sync daily/weekly/monthly".
- Always surface a "Last synced" line, including manual-only sources, via
a new lastSyncedAt input fed from Document.lastSyncedAt.
- Pair a relative age with an absolute date/time ("Synced 2h ago ·
Jul 3, 2:14 PM") for both "how long ago" and "exactly when".
Display-only; the timestamps already exist in the data model.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Aligns the knowledge-base sync control with the approved mockup and fixes a bug that rendered the status line as a bare "Synced · next sync". Layout: - Move the "Auto-sync from <source>" context out of the control's button row (where it forced "Pause" to wrap) and into the file's meta line. - Compact the control to just the schedule select + actions. - Add a status-line dot (green healthy / amber attention / grey idle). - Top-align the status icon and download/trash actions with the filename (items-start) instead of floating against the taller row. Timestamp bug: several generators built ISO strings as `datetime.isoformat() + "Z"`, yielding "…+00:00Z" (offset AND Z). That is invalid ISO 8601 and parses to Invalid Date in strict engines (Safari), so last-sync / next-sync rendered blank. Normalize to a single trailing Z in service, dispatcher, and worker; harden the dispatcher parser to always return a UTC-aware datetime; and harden the SPA to tolerate the legacy "+00:00Z" so already-persisted policies still render. Download and delete controls on documents are preserved. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The web-source (crawl) row shared the sync control with documents but was missing the parity bindings the document row got: no source-context line and no persistent last-synced timestamp, so a manual web source rendered as a bare dropdown while an identical document showed full context. - Add the meta-line context "Auto-sync from the web" (guarded by isCrawlSyncable), mirroring the document's "Auto-sync from <provider>". - Feed the control lastSyncedAt from crawl.completedAt so a manual web source still shows when it last refreshed (the component already prefers the policy's lastSyncAt when a schedule is active). - Make the crawl meta line wrap-safe (flex-wrap), matching the doc row. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The Web sources list had no loading flag, so it popped into existence after its network round-trip, while Uploaded Documents showed a lone centered spinner — two async lists with inconsistent, jarring loads. Introduce one initial-load gate for both lists: - Add isLoadingCrawls, set from loadSyncData (cleared on the viewer early-return and in finally), mirroring isLoadingDocuments. - isLoadingKnowledge computed gates both lists so they reveal together; raise both flags synchronously in ngOnInit so the first paint is the skeleton, not an empty flash. - Replace the documents spinner with a skeleton list (pulsing icon + two text bars, varied widths) that mirrors the final row shape — no layout shift, and consistent with the existing connector-button skeleton. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Changing the schedule (e.g. Don't auto-sync → Sync weekly) held the busy state for the whole create/update/delete round-trip but only disabled the controls — there was no positive sign anything was happening, and the select eagerly reverts to its old value until the mutation confirms, so it read as "nothing happened". While busy, the status slot now shows a spinner + "Saving…" (role=status), taking precedence over the sync status line — including for a manual-only source being enabled, where no policy or status exists yet. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…status-clarity fix(kb-sync): clarify the auto-sync control and repair blank sync timestamps
UserSyncService built timestamps as `datetime.isoformat() + "Z"`, yielding "…+00:00Z" — both an offset AND a Z. That is invalid ISO 8601 and parses to Invalid Date in strict engines (Safari), so the admin user-list "Last login" and user-detail "Created"/"Last login" dates (rendered via `new Date()`) showed "Never". Same class of bug just fixed across KB-sync (service/dispatcher/worker); this applies the identical normalization to the users domain. - Write path: add `_iso()` helper, normalize `created_at`/`last_login_at`. - Read path: add `_heal_iso()` and apply in `_item_to_profile` / `_item_to_list_item` so legacy "+00:00Z" rows render correctly. Necessary because `created_at` is preserved across logins forever (never rewritten), so pre-fix users would otherwise show "Never" in Safari permanently. - Regression tests for the write-path invariant and read-path heal. `last_login_at` also backs GSI2SK/GSI3SK (sort-by-last-login); the suffix change is lexicographically safe — ordering is dominated by the microsecond datetime prefix and a transient mix of "+00:00Z"/"Z" rows does not corrupt it. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…mestamp-zulu fix(users): normalize sync timestamps to strict ISO 8601 (single Z)
…panel Wrap the Knowledge base section on the Assistant editor in a rounded, bordered inset with a subtle gray-100/60 fill instead of the flat border-t divider shared by other sections. Against the gray-50 form column this reads as a mildly contrasted group, and the white inner lists (Web sources / Uploaded Documents) now float on the tint to reinforce the grouping. Drops the border-t/pt-8 divider since the card provides its own separation; the form's space-y-8 preserves the top gap. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Add Drive, Docs, Gmail, and Calendar icon assets under public/logos. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Add Google connector logo SVGs (Drive, Docs, Gmail, Calendar). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
… + spike brief Reframe the proactive-agent effort from a single "Oliver" feature into a primitive-enablement plan, mapped onto the Harness (headless run entrypoint) and Registry (catalog + governance) explorations. - agentic-platform-primitives.md: primitive maturity + gap ledger, the six fundamentals (F1 headless run entrypoint .. F6 registry/governance), phased plan, and Harness/Registry overlap. Oliver demoted to one validation use case among several. - scheduled-agent-runs.md: detailed design for F1+F2+F3 (renamed from the earlier Oliver draft; generalized so any config/prompt/cadence works). - harness-entrypoint-spike-brief.md: tight spike brief for the F1 keystone (unattended-as-user auth + server-side SSE), to run in dev-ai. Resolved decisions: F1 = minimal internal run_agent_headless(), A2A-ready; governance floor (F6a) pulled forward into Phase A, Registry discovery (F6b) deferred. Open: KB decoupling appetite, floor depth, sequencing. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…primitives-plan docs(specs): agentic platform primitives plan (+ scheduled-runs design + F1 spike brief)
…ev-ai run_agent_headless in apis/shared/harness: per-owner Cognito bearer mint (workload-token + SigV4 front-door paths proven dead at the runtime gateway), server-side SSE drain pinned to live wire shapes, F6a audit records + guardrails/classification seams, delivery via the runtime's own session materialization + title override. Driver script reproduces the dev-ai proof and the negative auth probes. Findings + Phase A design in docs/specs/harness-entrypoint-spike-findings.md. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The headless-run entrypoint spike (Fable 5) is GO — proven end-to-end in dev-ai. Bring its findings doc onto develop as the decision record and lock the four gate decisions into the plan of record. - Add harness-entrypoint-spike-findings.md (design deliverables + Phase A punch list; full spike code lives on branch spike/harness-headless-entrypoint). - agentic-platform-primitives.md §6: resolve act-as-user auth policy (Cognito per-owner token behind an explicit headless-grant record), F6a floor depth (audit fail-closed now; PII checkpoint required before unattended schedules), KB decoupling (defer), sequencing (proactive-spine-first confirmed). - scheduled-agent-runs.md §8: mark unattended-auth resolved with the chosen path. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
… primitive Replace the spike's BFF-table Scan with an explicit headless-grant record (apis/shared/harness/grants.py): create-on-enable from an attended session, per-owner lookup via the sparse HeadlessGrantUserIndex GSI, revocation that deletes the stored credential, and a documented "must have logged in within 30 days" policy (TTL anchored to the login that issued the pinned refresh token, matching the Cognito refresh-token validity). CognitoRefreshBearerAuth now mints from the grant (rotation-aware: a rotated refresh token is persisted back before the mint returns — punch-list #5). Dedupe build_invocations_url into the harness as the single canonical resolver; the chat proxy imports it (punch-list #4). Document the enabled_tools=None = all-RBAC-allowed semantic and the schedule-snapshot rule (punch-list #7). Governance docstrings updated to the locked F6a decision: audit-only fail-closed + wired no-op guardrail/classification seams, implementations gated to the scheduled phase. Add an _build_http_client seam and tests covering the grant lifecycle, grant-backed minting, audit fail-closed ordering, and stream outcomes against a MockTransport runtime. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…lity POST /runs/now executes one agent turn through the exact unattended path a scheduled run will use (create-on-enable grant -> per-owner Cognito mint -> runtime /invocations -> server-side SSE drain -> governance floor -> session materialization) — the PR-1 validation surface from docs/specs/scheduled-agent-runs.md §7. GET/DELETE /runs/grant expose grant status and total revocation. Gating is two independent controls (spec §6): the SCHEDULED_RUNS_ENABLED kill switch (default ON; only the literal "false" disables — empty workflow vars can't dark-stop prod) and a new `scheduled-runs` RBAC capability resolved through the mature tools grant axis (apis/shared/rbac/capabilities.py) — GA = grant the id to the default role. Auth is the standard SPA cookie dependency per the CLAUDE.md app-api rule; mint failures surface as 409, never 401, so the SPA is not bounced through the login redirect. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Thread the scheduled-runs kill switch through CDK: scheduledRuns.enabled in config.ts (the CDK_KB_SYNC_ENABLED empty-string-safe ternary, copied exactly) -> SCHEDULED_RUNS_ENABLED on the app-api container -> CDK_SCHEDULED_RUNS_ENABLED forwarded by platform.yml. Default ON with a kill switch; nightly inherits the default. Add the sparse HeadlessGrantUserIndex GSI (grant_user_id / created_at) to the BFF sessions table backing apis/shared/harness/grants.py — only HEADLESS-GRANT# items carry the partition attribute, so session rows never project into it. App-api's existing table grant already covers index/*, so no IAM change. Tests mirror the kbSync flag matrix and pin the GSI shape. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…ke-findings-and-decisions docs(specs): F1 spike findings + locked decision-gate calls
…phase-a feat(harness): Phase A — headless run entrypoint + Run now (PR-1)
…isite Records the work breakdown, model tiering, and the one design fork gating scheduled delivery: the F6a classify_output checkpoint must run in-loop (or as a post-hoc scrub), not pre-delivery, because the runtime turn persists the session during the turn. B1 (inert schedule CRUD) is safe to start now. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…0 PII gate A headless run executes as the owner with the owner's RBAC and delivers only to the owner's own session list, so it crosses no new access boundary and introduces no new recipient. Governance = RBAC (run-as-user) + grant lifecycle + quota + fail-closed audit — all already built. The content classification pass adds nothing for deliver-to-self, so B0 collapses and B2 delivery is unblocked. Revises primitives §6-4 and the Phase B brief §1. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Add ScheduledPrompt model + ScheduledPromptService (apis/shared, mirroring sync_policies) and app-api CRUD under /schedules — create/list/get/update/ pause/resume/delete, gated by SCHEDULED_RUNS_ENABLED + the scheduled-runs RBAC capability. Cadence (daily/weekday/weekly) -> next_run_at is computed timezone-aware in the service so the future dispatcher stays a dumb "who's due" query. enabled_tools is snapshotted at creation (Phase A punch #7). Storage rides the existing sessions-metadata table (PK=USER#{user_id}, SK=SCHEDPROMPT#{schedule_id}), with a new sparse DueScheduleIndex GSI (GSI3_PK/GSI3_SK, distinct from SessionLookupIndex's GSI_PK/GSI_SK to avoid attribute collision) projected only while state=="active". app-api already holds CRUD+index/* IAM grants on this table, so no IAM changes were needed. Deliberately inert: nothing fires yet. The dispatcher/worker that reads DueScheduleIndex and calls run_agent_headless is B2. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Resume turns (interrupt_responses set — OAuth-gated MCP consent or tool-approval) crashed with `NameError: cannot access free variable 'effective_enabled_tools'`. The variable is referenced unconditionally by the `stream_with_quota_warning` streaming closure (attachment guidance + tabular inventory) but was only assigned in the non-resume branch. On resume the closure raised before its first yield, the inference-api container returned 500, and the AgentCore Runtime data plane translated that into a 424 Failed Dependency to app-api and the SPA. This broke every interrupt-resume turn since the agent-designer tool-binding refactor (0b9b039) — most visibly "connect to Gmail for employees", which completes via an OAuth-consent resume. Bind effective_enabled_tools from the paused-turn snapshot on the resume branch (the same source the resume get_agent call uses). Adds a resume-path regression test to tests/routes/test_inference.py that drives /invocations with interrupt_responses and asserts a 200 stream; without the fix it fails with the NameError. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…er-resolution fix(agents): resolve model provider for agent-bound invocations
…tive-enabled-tools-nameerror fix(inference): bind effective_enabled_tools on resume path (prod 424 on OAuth-gated MCP connect)
…nto-develop-1.6.1 Backmerge: main into develop (1.6.1)
…2026-07-10 chore(kaizen): weekly research scan 2026-07-10
Generated by the kaizen-research skill. Top 5 ideas appended to docs/kaizen/review-queue.md for the kaizen-review-prep run later this morning. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Root-cause spec for the SessionMetadata parse-failure warnings: the session
row's sort key encodes lastMessageAt, forcing a delete+put row move every turn.
Concurrent writers race that move and upsert bare ghost rows. Also drives the
first-turn duplicate-row race.
Fix: static SK (S#{session_id}) + sparse SessionRecencyIndex GSI for recency
listing. Covers the expand -> migrate -> backfill -> contract migration,
downstream/forked-deployment safety (marker gate + graceful GSI-missing
fallback), pagination-token compatibility, and the test matrix.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…175 Phase 0) Sparse recency index (GSI4_PK=USER#{id}, GSI4_SK={lastMessageAt}#{session_id}, projection ALL) for newest-first active-session listing once the base sort key becomes static. Phase 0 of the static-sort-key migration: adding the index is a no-op until rows populate GSI4 keys, so it deploys safely ahead of any code change. IAM already covers it via the SessionsMetadataAccess /index/* wildcard. Update tables-detailed test to assert all four GSIs (the "2 GSIs" title was already stale after DueScheduleIndex) and the new index's key schema. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…metadata-static-sk-phase0 feat(infra): SessionRecencyIndex GSI on sessions-metadata (issue #175 Phase 0)
…Phase 1a)
Expand-read step of the static-sort-key migration. list_user_sessions now reads
the UNION of two disjoint sources so a session is visible whether or not its base
sort key has been migrated to the static S#{session_id} form:
- legacy (un-migrated): base table, SK begins_with 'S#ACTIVE#'
- migrated: SessionRecencyIndex GSI (GSI4_PK=USER#{id}, GSI4_SK={lastMessageAt}#{id})
Pagination switches to a value cursor ({lastMessageAt}#{session_id}) so each page
is derived independently from the last returned position, with no cross-page
buffering; fetching limit+1 valid rows per source is provably enough to detect a
next page. The cursor decoder is tolerant — legacy/undecodable tokens fall back to
first page (a harmless reset across the deploy boundary).
Degrades to legacy-only if SessionRecencyIndex doesn't exist yet (code ahead of the
CDK GSI): the GSI query's ResourceNotFoundException is caught.
No writes change and no row migrates in this phase — this only teaches every reader
to cope with both schemes, which must be fully rolled out before Phase 1b turns on
self-migrating writes.
Tests: union ordering, cross-union pagination (no dupes/gaps), migrated-only via
GSI, ghost/preview skip, and graceful fallback when the index is absent. conftest
sessions_metadata_table fixture gains the SessionRecencyIndex GSI to match prod.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…metadata-static-sk-phase1a feat(sessions): dual-scheme union read for session listing (issue #175 Phase 1a)
Auto prompt caching (CacheConfig strategy=auto) appended its cachePoint after the last user message's content, so any turn attaching a non-PDF document (txt/docx/csv/...) sent [text, document, cachePoint] and Bedrock's Anthropic adapter rejected it with "ValidationException ... messages.N.content.M.type: Field required", surfacing to users as "Agent force-stopped" (prod incidents Jul 14-16, e.g. session dd1a647a on a .txt transcript upload). strands 1.48.0 places the cache point before the first non-PDF document block instead (upstream issue #1966); every placement it produces was verified live against global.anthropic.claude-sonnet-4-6 ConverseStream. Also corrects the model_config comment that credited PR #1438/1.39.0 with this fix - #1438 was the auto-caching feature itself. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…ce-stop-validation-791e6f fix(deps): bump strands-agents to 1.48.0 for cachePoint-attachment fix
…2026-07-17 chore(kaizen): weekly research scan 2026-07-17
… missing GSI (issue #175) The Phase 1a dual-scheme read (PR #667) catches a missing SessionRecencyIndex to fall back to legacy-only listing, but only handled ResourceNotFoundException — what moto raises. Real DynamoDB raises ValidationException ("The table does not have the specified index") for a missing GSI (verified against the prod table). So if the 1a backend deployed to an environment before the CDK GSI existed, list_user_sessions would 503 instead of degrading. Broaden the catch to also handle ValidationException (scoped by the "specified index" message so genuinely malformed queries still surface). This restores the intended order-independence: 1a is safe whether or not SessionRecencyIndex exists yet, which matters for prod deploy ordering (backend.yml vs platform.yml) and for forked deployments. Add a test that reproduces the real ValidationException on the index query (moto masks it), asserting fallback to legacy-only results. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Provision a full Word (.docx) toolset behind the single create_word_document capability toggle. Each tool runs python-docx in Bedrock Code Interpreter and uses the existing user-files store (S3 + DynamoDB) for persistence and delivery. - create/modify/list/read tools in agents/builtin_tools/word_document_tool.py, injected per-request via _build_word_document_tools (inference_api/chat/routes.py). - Frontend inline-visual 'word_document' renderer with an accessible download button (Tailwind utilities, no scoped CSS). - Restore-time content-block sanitizer in TurnBasedSessionManager: drops empty/typeless blocks from restored history that caused Bedrock ConverseStream 'messages.N.content.M.type: Field required'. - Seed create_word_document in bootstrap DEFAULT_TOOLS ('Word Documents') + updated seed tests.
…ument-tools Add Word document tools (create/modify/list/read)
…-missing-index-fallback fix(sessions): degrade to legacy-only on real ValidationException for missing GSI (issue #175)
…nto-develop-1.7.0 Backmerge: main into develop (1.7.0)
…rotation (issue #175 Phase 1b) Turns on the write side of the static-sort-key migration. Sessions stop encoding lastMessageAt in the sort key, so the row never moves and the ghost-row race that produced "Failed to parse session item" warnings is structurally eliminated for every migrated row. Changed (all resolve the row via GSI, which is SK-scheme-agnostic): - ensure_session_metadata_exists: new sessions born at static SK S#{id} + GSI4 keys, with a real attribute_not_exists(PK) conditional put. The deterministic SK makes the guard meaningful, closing the first-turn duplicate-row race the old timestamped SK made impossible to gate. - update_session_activity: drops the per-turn Phase-B rotation. Static rows update in place (SET GSI4_SK re-positions the sparse recency index — no row move); a still-legacy row does its one-time final rotation to the static SK, carrying any concurrent write. - _store_session_metadata_cloud: static SK; migrate legacy->static on move; SET GSI4 for active, REMOVE for deleted; never un-migrates a static row. - session_service.delete_session: resolves the raw SK via _get_session_by_gsi instead of reconstructing S#ACTIVE#{lastMessageAt}#{id} (which misses migrated rows). Non-rotating soft-delete: SET status=deleted + REMOVE GSI4 in place, or migrate a legacy row to a static tombstone. Drops the S#DELETED# prefix (nothing reads it). The ~10 other writers resolve-then-update-in-place on the current SK and need no change — they already work on a static SK and never rotate. Tests: TestWriteSideMigration (born-static, one-time migrate, no rotation, soft-delete in-place/legacy, end-to-end create->activity->list->delete) plus the real ConditionalCheckFailedException contract (moto raises it for a failed conditional put). Updated three tests that encoded the old rotation contract. Full shared+routes suites: 1689 passed. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…metadata-static-sk-phase1b feat(sessions): static-SK write path — born static, self-migrate, no rotation (issue #175 Phase 1b)
The user-files S3 client pinned its endpoint to https://s3.{AWS_REGION}.amazonaws.com. In the AgentCore Runtime AWS_REGION does not reliably match the bucket region, and the explicit endpoint_url disables botocore's automatic S3 region redirect, so PutObject failed with PermanentRedirect. Resolve the bucket's real region via HeadBucket (x-amz-bucket-region header; maps to s3:ListBucket, which the runtime role already has — GetBucketLocation is not granted) and pin the client to it, dropping the hardcoded endpoint_url. Fixes both the save and the presigned download URL region.
…t-s3-region Fix S3 PutObject PermanentRedirect in Word document tools
Patch release fixing a Word-document save failure in the AgentCore Runtime and advancing the session-metadata static-sort-key migration (issue #175) to its write side. - Fix: Word-document PutObject no longer fails with PermanentRedirect — the user-files S3 client resolves the bucket's real region via HeadBucket instead of pinning to AWS_REGION, and drops the hardcoded endpoint_url (#674) - Improved: static-sort-key write path for session metadata — new sessions born static, legacy rows self-migrate in place, no per-turn rotation; eliminates the ghost-row race and closes the first-turn duplicate-row race (issue #175 Phase 1b) (#673) Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
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.
Patch release. Cut from
develop, targetsmain(squash-merge). Backmerge intodevelopfollows once this merges.What's in it
🐛 Fixed — Word-document
PutObjectPermanentRedirect(#674)The user-files S3 client pinned its endpoint to
https://s3.{AWS_REGION}.amazonaws.com, but the AgentCore Runtime'sAWS_REGIONdoesn't reliably match the bucket region and the explicitendpoint_urldisabled botocore's automatic region redirect — so Word-document saves failed withPermanentRedirect. The client now resolves the bucket's real region viaHeadBucket(x-amz-bucket-region, backed by thes3:ListBucketthe runtime role already has) and drops the hardcodedendpoint_url. Fixes both the save and the presigned download URL.✨ Improved — static-sort-key write path for session metadata (issue #175 Phase 1b) (#673)
New sessions are born at a static sort key (
S#{session_id}+SessionRecencyIndexkeys) behind a realattribute_not_exists(PK)conditional put; legacy rows self-migrate in place on their next write. Rows no longer rotate on every message, so the ghost-row race behind the "Failed to parse session item" warnings is structurally eliminated for migrated rows, and the deterministic SK closes the first-turn duplicate-row race.delete_sessionresolves the raw SK via the GSI and soft-deletes in place.Release mechanics
VERSION1.7.0 → 1.7.1;sync-version.sh --checkpasses (manifests + lockfiles regenerated).CHANGELOG.md+RELEASE_NOTES.mdupdated, new entry on top, previous entries untouched.Deployment
Backend-only — ship through
backend.yml. No CDK deploy, no data migration (rows self-migrate; readers already tolerate both schemes as of 1.7.0). No breaking changes.🤖 Generated with Claude Code