Analisi dei 3 mockup (top-nav mega-menu, tema navy/oro, Inter, Lucide) → spec per
migrare l'UI dal V2 (Bootstrap Italia/sidebar) al V3: design system (token/chrome/
componenti), architettura migrazione (riusa api.js/auth/help/i18n/FAB, sostituisce
solo chrome+css), work-breakdown a fasi (F0 foundation+dashboard pilota; F1 flotta
1 agente/gruppo IA in worktree; F2 verifica), mappa IA, guardrail, acceptance per
pagina, decisioni utente aperte (§7). Mockup = fonte visiva, dati reali non mock.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Connettore di discovery cloud AWS parallelo all'agente di rete: elenca
EC2/RDS/S3 via AWS CLI (zero dipendenze npm) + flussi SG ingress aperti
verso 0.0.0.0/0, mappa in asset NIS2 e POSTa a /api/services/assets-ingest
(source=aws). Config via env (RESOURCES/AWS_REGION/MAX_ITEMS/DRY_RUN),
gestione errori robusta (aws assente o comando fallito → messaggio chiaro).
README aggiornato con sezione AWS + permessi IAM read-only minimi.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
segnala-anonimo.html: pagina PUBBLICA (no checkAuth, no sidebar, no common.js/api.js/
pwa.js, no analytics) che ospiterà l'iframe del canale Vocea, conforme alle 6 regole
anonimato StarGateWB: Referrer-Policy no-referrer (meta + iframe), CSP frame-src
app.vocea.cloud, slug del canale da ?canale=<slug> (nessun id del segnalante nell'URL),
postMessage con check event.origin + envelope {source:'vocea'} e NESSUNA persistenza
del recovery code (solo autosize). Dormiente finché non c'è lo slug/MS live.
+ docs/OUTGOING_TO_VOCEA_...: conferma origin frame-ancestors https://nis2.agile.software.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Ricevute da Vocea (27/6) le 6 regole per preservare l'anonimato del segnalante
nell'embed del widget. Gap analysis NIS2: la pagina app «Segnalazioni»
(whistleblowing.html) è dietro login → NON usabile come canale anonimo; serve una
pagina PUBBLICA pre-login. Più: no-log apertura, no id nell'URL iframe, check
event.origin, Referrer-Policy no-referrer, CSP frame-src app.vocea.cloud.
Origin da dare a Vocea per frame-ancestors: https://nis2.agile.software.
Da applicare all'attivazione Vocea (oggi scaffold dormiente).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
scripts/discovery-agent.mjs (Node 18+, zero dipendenze): scansiona CIDR/IP via
TCP-connect (no root), reverse-DNS, deduce tipo asset + criticità dalle porte aperte,
costruisce asset (external_ref=net:<ip>) + flussi di rete e li POSTa a
/api/services/assets-ingest col X-API-Key del connettore. Concorrenza, MAX_HOSTS,
DRY_RUN, isPrivate (RFC1918+loopback+link-local). + README_discovery_agent.md (uso,
variabili, cron, multi-connettore, estensione cloud, sicurezza).
Validato: node --check + DRY_RUN reale (listener temporaneo) → rileva porta aperta,
costruisce asset+flusso corretti.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Più connettori per azienda; ogni connettore ha una api_key dedicata (scope ingest:assets)
che l'agente esterno usa per mappare e auto-popolare NIS2.
- mig 064: discovery_connectors (per-org, multi) + discovery_runs (storico) + network_flows (ID.AM-03).
- DiscoveryConnectorController (org_admin): CRUD connettori + emissione/rotazione api_key
(mostrata 1 volta) + dettaglio con ultimi run.
- ServicesController::ingestAssets esteso: accetta anche "flows" (upsert network_flows,
dedup external_ref) e, se la chiave appartiene a un connettore, registra discovery_run
+ aggiorna last_run del connettore. Auto-scoring rilevanza NIS2 già in bulkUpsert.
- AssetController::bulkUpsert: ora salva anche "dependencies" → popola la Mappa Dipendenze.
- Router: /api/discovery-connectors (list/create/{id}/update/delete/rotateKey).
Smoke E2E (HTTP 201): 2 asset scorati + 1 flusso + run tracciato + dipendenze persistite.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Bug (segnalato "risponde mele per pere"): chiedendo ad ARIA "analizza la segnalazione
di Massimo Tagliavini e com'è stata gestita", ARIA FABBRICAVA un record falso (segnalante
"CISO", data "25 gennaio 2025", citazione inventata, gravità/stato/processo) — i ticket
dei singoli utenti vivono in AgileHub, non nei dati di ARIA → confabulava.
Fix: regola ANTI-INVENZIONE vincolante (priorità massima) nel system prompt di askWithRag:
non inventare MAI record specifici (segnalazioni/reclami/ticket/incidenti/persone/date/
citazioni/esiti) non presenti nei DATI ORG o nel contesto; i reclami/ticket dei singoli
utenti sono un sistema separato non accessibile → dichiararlo e indicare dove consultarli,
senza inventare. Smoke: ora ARIA NEGA correttamente l'accesso, nessun dettaglio fabbricato.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Click su un'intestazione (Nome/Tipo/Categoria/Criticita/Rilevanza NIS2/Owner/Stato)
ordina l'inventario; secondo click inverte (freccia ▲/▼, aria-sort).
Criticita e Rilevanza ordinano per valore reale, non alfabetico.
Client-side sull'inventario gia' caricato (no paginazione su assets), si combina
coi filtri, accessibile da tastiera. Help aggiornato + cache-buster help.js.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Nuovo pulsante "Esporta" in assets.html: scarica l'intero inventario
(tutte le pagine) in CSV con le stesse colonne riconosciute
dall'import (name, asset_type, criticality, data_classification,
internet_facing, dependencies_count, regulated, external_ref, vendor,
location). I criteri data_classification/internet_facing/
dependencies_count/regulated sono ricostruiti da relevance_criteria con
mappatura reversibile (round-trip stabile, verificato 100/100 casi).
Parser import reso quote-aware (RFC-style) per round-trip lossless di
nomi con virgola; retro-compatibile con CSV non quotato.
help.js + cache-buster (?v=20260625b) su tutte le pagine. version 1.24.14.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Completa i 2 punti rimandati dal supervisore:
- help.js sezione Inventario: documentate le nuove funzioni che il supervisore ha
aggiunto (commit 9c36604) → "Mappa Dipendenze" con il campo Dipendenze per popolarla;
"Elimina bene" (cestino, org_admin) distinto dallo stato "dismesso".
- AIService::navigationMapBlock: 2 regole nuove → (5) NON citare nomi file .html/URL
interni; (6) NON inventare pulsanti/funzioni/passi inesistenti, usare solo le funzioni
della guida/contesto della pagina. Elimina l'allucinazione "rischi.html" e simili.
Smoke: ARIA cita Elimina + Mappa Dipendenze, zero nomi-file, "Gestione > Rischi".
Cache-buster help.js -> 20260625a. php-fpm reload.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- Nuovo pulsante "Mappa Dipendenze" in toolbar: vista delle relazioni
tra i beni (costruita client-side da allAssets, endpoint backend gia'
esistente dependencyMap).
- Campo "Dipendenze" (multi-select) nel form bene: consente di compilare
la mappa che ARIA indicava ma che non aveva UI. create/update salvano
gia' il campo dependencies.
- Pulsante "Elimina" bene nella lista e nel dettaglio (DELETE /assets/{id}
gia' esistente, richiede org_admin).
- Dettaglio bene: dipendenze mostrate per nome anziche' per ID grezzo.
- version.json -> 1.24.13.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Richiesta: "AI deve sapere i contenuti dei dati a cui ha accesso l'utente".
- AIService::orgDataSnapshotBlock($orgId): aggregati LIVE dell'org (compliance score,
rischi aperti per livello + top 5, incidenti aperti, asset attivi/rilevanti NIS2,
fornitori critici scoperti, NC aperte, formazione, policy per stato). Query con
colonne reali (riuso DashboardController), ognuna in try/catch (degrada, non rompe).
- Iniettato in askWithRag SOLO se org_data_ok.
- SICUREZZA multi-tenant: AiController verifica membership in user_organizations
(super_admin bypassa) → org_data_ok; scope rigoroso organization_id → niente leak
cross-org da X-Organization-Id falsificato. PRIVACY: niente nome/fatturato (anonimizz.).
Smoke: con membership ARIA cita score 80% + 14 rischi (org 996003); senza membership
nega l'accesso ai dati. php-fpm reload.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Porting del pattern TRPG (CHANGE_aria_voice_pointer.md) sulla sidebar V2:
- public/js/aria-guide.js (nuovo): DESTINATIONS (26 voci menu → keyword IT),
matchDestination(question), pointTo(nav,label) = overlay + spotlight pulsante +
puntatore animato + coach-mark "Vai →"; Esc/click per chiudere; fallback navigate
se la voce non è visibile; rispetta prefers-reduced-motion; self-contained, no inline-CSS.
- common-bi.js: data-nav="<href senza .html>" sulle voci sidebar (aggancio puntatore).
- common.js: inietta aria-guide.js dopo login; dopo ogni risposta ARIA, se la domanda
combacia con una destinazione mostra il bottone "📍 Mostrami dov'è: <voce>".
Frontend-only, additivo. Cache-buster common.js/common-bi.js -> 20260624f.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Lamentela: "AI non allineata con help online e contesto". Causa: ARIA e Help
erano due fonti separate (Help=help.js, ARIA=KB) → divergevano.
Fix (fonte unica = help.js, zero duplicazione):
- help.js: getContextText(pageId) (testo piano dell'help della pagina) + detectPageId().
- common.js: invia page_id (canonico) + page_help (stesso testo del pannello "?") a /api/ai/ask.
- AiController: passa page_id/page_help in userContext.
- AIService::askWithRag: inietta "GUIDA CONTESTUALE DELLA PAGINA" nel system prompt
(ARIA si allinea all'help mostrato all'utente, non lo contraddice) + usa page_id
per il trigger relevanceGrid (più robusto del title).
Smoke: ARIA cita "registro dei rischi" dall'help + "Gestione > Rischi". Cache-buster
common.js/help.js -> 20260624e (39 pagine).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
La colonna operative_summary lato ticket-ms e' VARCHAR(2000): descrizioni
lunghe causavano l'errore SQL grezzo a video. Aggiunto maxlength + contatore
live + guard al submit nel widget locale public/js/bug-reporter.js.
Cache-buster: bug-reporter.js?v=20260624 in common.js + common.js?v=20260624
su tutte le pagine. Causa radice (dimensione colonna) inoltrata ad AgileHub.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
ARIA (askWithRag) conosceva solo il nome pagina + KB/fonti, non la griglia
reale delle tendine della finestra «Valuta Rilevanza NIS2» (AssetScoringService::GRID):
dava risposte generiche non corrispondenti alla finestra. Aggiunto
relevanceGridBlock() (6 criteri, opzioni, punteggi, desc dalla fonte di verita')
iniettato nel system prompt quando l'utente e nell'Inventario o chiede della
rilevanza. Additivo, nessun impatto su altri flussi.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- AssetScoringService::GRID: aggiunto campo 'desc' a tutte le 35 opzioni dei 6 criteri (significato operativo, additivo)
- assets.html: ogni combo mostra il significato dell'opzione selezionata + nuovo box 'Dettaglio punteggio per criterio' (live, anche alla riapertura di una valutazione salvata)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
ARIA citava voci di menu inesistenti (es. 'A4 - Analisi e Valutazione
del Rischio' / 'Fase 4.1 - Inventario') non corrispondenti alla barra
laterale, e non sapeva da quale pagina scrivesse l'utente.
- AIService::askWithRag: iniettata la mappa REALE della sidebar
(navigationMapBlock, sincronizzata con common-bi.js) con regole che
vietano di inventare menu/fasi/codici; aggiunto contesto pagina utente.
- AiController::ask: accetta e passa il campo 'page' dal frontend.
- common.js (ARIA FAB): invia la pagina corrente in /api/ai/ask.
- Cache-buster common.js 20260623->20260623h su tutte le pagine.
NB: questo commit include anche il fix#419 gia' applicato e live in
common.js (email bug-reporter recuperata via api.getMe quando assente
nelle claims JWT), finora non committato — incluso come backup.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Correzioni dallo smoke live su api.vocea.cloud (23/6, VIGILE/Vocea):
- tenantInfo(): /tenants/{slug}/info è sotto /api/wb (NON /api/wb/v1) → request() ora
accetta baseOverride; aggiunto $apiBase (base senza /vN). I 3 /integrations/* restano /v1.
- portalUrl(): path reale /{locale}/segnala/{slug} (era /wb/{slug}); aggiunto
portalStatusUrl() /{locale}/stato/{slug}; channelStatus espone portal_status_url.
- Vault namespace corretto: tier1__nis2-app__vocea (l'app reale è nis2-app, non nis2-agile).
Scaffold resta DORMIENTE (VOCEA_ENABLED=false). NB: VIGILE segnala che le route
/api/wb/v1/integrations sono ancora 404 (deploy MS incompleto) → non attivare finché
Vocea non completa il deploy e l'API key non è nel vault.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Aggiunti endpoint PUT/DELETE /api/assets/subclasses/{id} (org-scoped, le
sottoclassi di sistema restano protette; FK ON DELETE SET NULL scollega i
beni senza cancellarli) + link UI «rinomina»/«rimuovi» accanto a «aggiungi».
Cache-buster api.js + bump version 1.24.8.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
La valutazione rilevanza salva relevance_criteria come mappa piatta
{criterio: optionValue} (AssetController::score), ma showScoringModal
leggeva prev[key].value (forma a oggetto) -> sempre undefined -> nessuna
combo pre-selezionata alla riapertura. Letto ora il valore-stringa diretto,
con fallback retro-compatibile alla forma a oggetto {value:...}.
Fix solo frontend (JS inline in assets.html), nessuna migrazione.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Risolve #418/#420 (ARIA sembrava non rispondere / serviva riaprire il pannello):
l'attesa della risposta /api/ai/ask (RAG+Claude, alcuni secondi) non mostrava
alcun indicatore -> ora compare 'ARIA sta scrivendo...' con input disabilitato
(anti doppio-invio). Indicatore rispetta prefers-reduced-motion (AGID/WCAG).
Risolve #425: il campo di scrittura era <input> a riga singola -> ora <textarea>
con Invio=invia, Shift+Invio=a capo, auto-crescita e aria-label.
common.js e' statico (no PHP/USR2): bump cache-buster ?v=20260623 su 39 pagine
(public + admin) + version.json 1.24.6.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Scaffold per sostituire il modulo whistleblowing interno (plaintext) con Vocea
(nexus-whistleblowing-ms), prodotto zero-knowledge della suite. Modalita C
(integrazione verticale, nessuna crypto re-implementata). DORMIENTE: VOCEA_ENABLED
=false di default -> zero impatto sul modulo legacy.
- docs/sql/063_vocea_channel.sql + application/cli/migrate_063_vocea_channel.php
(idempotente): organizations += vocea_tenant_slug/channel_status/enrolled_at.
NON ancora applicata al DB.
- application/services/VoceaService.php (nuovo): client API integratori (X-API-Key
su api.vocea.cloud) per submit/status/reports/keyholders + portalUrl (embed
wb-link.js). Guard enabled()/isEnabledForOrg(): se OFF/non provisioned ritorna
DISABLED/NOT_PROVISIONED senza chiamate di rete.
- config.php: costanti VOCEA_ENABLED/BASE_URL/API_KEY/PORTAL_URL (default off).
- WhistleblowingController: branch dormiente in list() (delega a Vocea se attivo) +
endpoint GET channelStatus + helper voceaOrg/listViaVocea. Modulo legacy invariato.
- public/index.php: route GET:channelStatus.
- docs handoff: richiesta a VIGILE (OUTGOING) + risposta VIGILE (INCOMING).
Attivazione a MS live: applicare migrate_063 + VOCEA_ENABLED=true + VOCEA_API_KEY
(vault tier1__nis2-agile__vocea__*) + slug canale su organizations.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Anagrafica: sede legale Piazza di Campitelli 2, 00186 Roma (address/city); sede operativa Strada Scaglia Est 15, 41121 Modena (mission). Scope/contesto SGSI resi precisi: sedi registrate ma modello work-from-anywhere. Anagrafica Nuova Agile ora completa (P.IVA + sedi + classificazione + DPO).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Dati Direzione: fatturato 0,9M€(2026)/1,4M€(2027), ~12 persone → SOTTO-SOGLIA. Cloud provider (Allegato I) ma soggetto al cap dimensionale → NON obbligata per legge; la Direzione DELIBERA l'adozione VOLONTARIA integrale delle misure da soggetto 'importante'. Org: turnover+voluntary_compliance=1+designation_basis=voluntary. Manuale sez.11 'Caveat' → 'Determinazione di applicabilita NIS2' (+snapshot). NCR-9001 (maggiore) CHIUSA + CAPA-1010 completata. DPO: esterno deliberato (nominativo in corso) → NCR-9005 resta in correzione.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
feedback_reports += client_surface/client_version/client_build; FeedbackController/Service li salvano; feedback.js li invia (surface web-v2 + version/build da version.json). Standard hub_standards id=30. Smoke verde (provenance salvata, test pulito). NB: il widget feedback.js è oggi dormiente (rimosso dall'iniezione 14/6) → plumbing pronto per la riattivazione.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Dopo la fusione 36->12, corretti i rinvii nei 12 documenti pubblicati: 'Politica per la Sicurezza delle Informazioni' -> 'Politica Generale...'; rinvii a 'Procedura di Controllo dei Documenti' -> 'Procedura di Governance del SGSI'; rinvio all'Istruzione Hardening (assorbita) -> regole di hardening nella Politica Uso Accettabile/Dispositivi/Remoto; riscritta la sez.8 'Struttura documentale' del Manuale sui 12 documenti. Aggiornati anche gli snapshot v2.0. Auto-referenze interne (minuscole) lasciate invariate.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Approccio ISO 27001 agile/lean: i 36 documenti v1.0 sono fusi per tema in 12 documenti consolidati v2.0 (Manuale, 6 politiche, 5 procedure), pubblicati. I 36 originali sono ARCHIVIATI (storico preservato, non cancellati). Idempotenza ignora gli archiviati (fix collisione titolo procedura incidenti).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
I diritti sul ciclo di vita dei documenti SGSI sono ora configurabili per ruolo (matrice Ruolo x Azione, org-scoped) invece che cablati.
- mig.061: tabella isms_doc_permissions (org x capability x role x allowed)
- IsmsModelController: requireDocCapability() sostituisce i requireOrgRole cablati nelle 7 transizioni (edit/submit/approve/publish/reject/archive/new_version); default = comportamento precedente; super_admin sempre ok; endpoint GET/PUT /api/isms/docPermissions (canEdit)
- settings.html: tab 'Permessi documentali' con matrice checkbox (lettura per i membri, salvataggio Amministratore/Direzione)
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- exportAllDocumentsWord: GET /api/isms/documentsWordAll -> .doc unico (copertina+indice+page-break) di tutto il set non archiviato
- getDocument: GET /api/isms/documents/{id} (full doc per editor)
- updateDocument: guard NOT_EDITABLE su documenti non-bozza/revisione + ruoli estesi a board_member
- UI isms.js: pulsante 'Scarica tutto (Word)', 'Modifica' (editor contenteditable + toolbar + toggle HTML) per bozze/revisione
- help.js: editor in-app + download unico
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Ogni documento del Modello SGSI ha un ciclo di vita completo Bozza->In revisione->Approvato->Pubblicato->Archiviato con tracciamento (revisore/approvatore/pubblicatore, date, entrata in vigore, prossimo riesame) e storico versioni (snapshot). Download Word .doc modificabile per documento.
- mig.060: stati published/archived + 10 colonne tracciamento + tabella isms_document_versions
- IsmsModelController: submit/approve/publish/reject/archive/newVersion/versions/exportWord + listDocuments arricchito
- index.php: 8 route documents/{id}/(submit|approve|publish|reject|archive|newVersion|versions|word)
- isms.js: UI badge stato + pulsanti transizione + download Word + storico (IT/EN inline); cache-buster
- help.js: sezione 'Ciclo di vita dei documenti (cl.7.5)'
- 31 documenti di Nuova Agile pubblicati v1.0 (build_nuova_agile_p6_publish)
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Audit registrato live nel modulo Audit interni: 21 voci di checklist (clausole ISO 27001 4-10 + controlli Annex A/SoA + misure NIS2 art.23/24/25), 5 Non Conformità aperte (1 maggiore: determinazione applicabilità NIS2 non formalizzata; 4 minori: trattamento rischi, formazione, controlli non eseguiti, DPO non nominato). Verdetto: SGSI documentato ma non ancora operativo, non pronto per Stage 2.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Provisioning idempotente di un'azienda cliente cloud-only (Aruba/Hetzner/AI, no server in sede, PC portatili):
- Fase 1: org + classificazione NIS2 (important, sub_threshold_candidate/preliminary) + 3 membri esistenti collegati + organigramma 8 ruoli
- Fase 2: 12 asset (NIS2-rilevanti) + 4 fornitori + 10 rischi + gap analysis (80 risposte, 45%)
- Fase 3: ISMS model + SoA 111 controlli (27001/27017/27018) + 31 documenti (Manuale/10 politiche/13 procedure/7 istruzioni)
- Fase 4: 15 piani di controllo periodici + 5 corsi formazione
Documenti AI-redatti con regole fonti-certe (D.Lgs.138/2024 art.23/24/25; 24h/72h/1 mese). Sorgenti in _docs_na/.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Problema: il supervisore autonomo non leggeva gli allegati (nel payload ticket solo testo
'N allegati aggiunti', niente metadati/URL). Endpoint esistenti ma non documentati nel runner:
GET /tickets/{id}/attachments[/{attId}]. Suggeriti 5 miglioramenti cross-suite (owner VIGILE):
esporre attachments nel payload; pre-download nel runner standard; doc endpoint; nota anti-injection.
Fix locale NIS2 già applicato (prompt supervisore).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>