synheart_core 0.7.0
synheart_core: ^0.7.0 copied to clipboard
Flutter SDK for the Human State Interface (HSI) 1.3 — unified collection of wearable, behavior, and phone signals with consent-gated cloud upload.
Changelog #
All notable changes to this project will be documented in this file.
The format is based on Keep a Changelog, and this project adheres to Semantic Versioning.
Unreleased #
0.7.0 - 2026-06-17 #
Added #
HSIAxes.stress— typed accessor for the engine's multimodal stress reading (engine v0.10.0; HSI 1.3axes.affective[].stress). Hosts get a named field instead of digging throughrawJson. Resolves tonullon the legacy 1.2 path that never carried it. Parity with the Kotlin/Swift bindings.EdgeIngest— canonical phone-side consumer of the Synheart edge wire contract (watch → phone). Pure Dart (no Flutter import), unit-tests underdart test/flutter test. Parseshr_sample/bio_sample/hsi_artifact/ session events and, for artifacts, dedupes byartifact_id, verifiespayload_hash_sha256== sha256(payload_json), validates the innerhsi_versionagainst the supported set, and produces theartifact_ackbody. Public surface:- Sealed
EdgeEventfamily (HrEvent | BioEvent | ArtifactEvent | SessionEventWrap) plus the typed models (HrSample,BioSample,HsiArtifact,Accel) and theEdgeOutcomeresult family. - Reactive
eventsbroadcastStream<EdgeEvent>, emitting in lock-step with the optionalEdgeIngestListenercallbacks (parity with the KotlinSharedFlowand Swifteventspublisher). - ACK helpers
drainPendingAcks()/buildAckBody(...)/drainAckBody(). dispose()— Flutter-only lifecycle that closes the broadcastStreamController(idempotent). The Kotlin/Swift hot streams have no equivalent. Hosts usingEdgeIngest.eventsmust calldispose()when done.- The Kotlin-only
onUnsupportedHsiVersion/onHashMismatchhooks are folded into theEdgeOutcomereturn value (ArtifactHashMismatch) and logging, plus the optional poison-pill / dead-letter hookEdgeIngestListener.onPoisonPill(artifactId, expected, actual, attempts). - Delivery hardening (the watch outbox is delete-on-ACK):
- Duplicate re-ack — a duplicate
artifact_idis not re-surfaced (ArtifactDuplicate) but is re-queued for ACK, so a lost ACK no longer makes the watch resend forever. - Bounded dedupe set — the seen-artifact set is a bounded LRU
(
seenLruCapacity), keeping memory flat over a long-lived process. - Poison-pill dead-letter — an artifact that fails hash verification
poisonPillThreshold(3) times for the same id is dead-lettered (ArtifactDeadLettered, theonPoisonPillhook, and ack-to-discard) so a deterministically-corrupt artifact stops blocking the outbox.
- Duplicate re-ack — a duplicate
- Sealed
Notes #
- The native runtime binary is proprietary and authentication-gated. This
pub.dev package is a thin FFI shell; it does not bundle the Synheart
Runtime native binary. The binary is distributed from a private artifact
registry and requires Synheart authentication (
synheart login) — it is not available from public package sources. An unauthenticatedflutter pub add synheart_coregets a non-functional FFI shell until the binary is installed via the Synheart CLI (synheart install runtime). See the README's "Native Runtime Setup".
0.6.3 - 2026-06-07 #
Added #
Synheart.requestStudyDataDeletion({bool dryRun}): request erasure of the data the participant contributed to their study for this app — the deletion the consent copy promises alongside withdrawal. No identifiers are passed; the participant and app come from the device's signed cloud credential.dryRunreturns an inventory preview without deleting; a real request is accepted asynchronously and carries arequest_id. Idempotent.
0.6.2 - 2026-06-07 #
Added #
- Research-study enrolment API:
Synheart.enrolResearchStudy(...),Synheart.validateResearchStudyCodes(...), andSynheart.withdrawResearchStudy(). Enrolment rides the device's signed cloud credential — no tokens are handled by the app. Withdrawal is idempotent. DeviceAuthConfig.packageName: the app's package / bundle id, passed through so device registration can be bound to the app. Optional (defaults to empty).
0.6.1 - 2026-05-24 #
Added #
- Consent surface for Syni.
ConsentForm.syni, the parameterSynheart.grantConsent(..., syni: bool), andConsentSnapshot.syniare now exposed end-to-end so hosts can track and persist the user's consent for on-device or cloud LLM processing alongside the other channels. Backward compatible —synidefaults tofalseeverywhere so existing callers compile unchanged. - Canonical channel iteration. New
ConsentTypeMetaextension onConsentTypeexposeswireKey,displayName,valueOn(typed accessor forConsentEffectiveState), andvalueOnForm(typed accessor forConsentForm). PlusConsentEffectiveState.toChannelMap()/ConsentForm.toChannelMap()andConsentForm.fromChannelMap(...). Hosts can now iterateConsentType.valuesrather than hardcoding each channel — adding a new channel becomes a single change in the SDK enum plus its metadata; consumers pick it up automatically. Synheart.consentChanges— public broadcast stream ofConsentSnapshots. Emits on any grant or revoke so hosts that render consent in multiple places (e.g. a profile counter alongside a consent screen) can stay in sync without pollingconsentEffectiveStateTypedor re-dispatching reads.Synheart.syni.bindPersona(SyniPersona)(passthrough topackage:syni's newSyniAgent.bindPersona). Cloud chat needs only a persona plus a configured cloud client; hosts that pickSyniExecutionMode.cloudOnlycan call this once instead of runninginstall()purely to attach a persona. Requiressyni: ^0.3.2.
Changed #
synidependency bumped to^0.3.2(carriesbindPersona+ the fix that letscloudOnlychat run without a local install).ConsentEffectiveState.hasAnyGrantnow includessyni— previously the flag was tracked through the rest of the state but missing from this convenience getter.
Fixed #
consentSubmitFormTypednow propagates the form'ssynivalue to the runtime via the per-type grant/revoke FFI when the runtime's form parser doesn't carry the key directly. Without this a host settingsynion the typed form would see the other channels update butsynistay at its previous value.
0.6.0 - 2026-05-23 #
Added #
Synheart.configure()now wires Syni's hybrid router to the cloud — it builds theSyniCloudConfigand authenticates Syni cloud-chat requests with anX-Synheart-Proofdevice-attestation header (produced by core-runtime).Synheart.synicloud chat needs no extra host-app setup.DeviceAuthProvider.signUrl— builds anX-Synheart-Prooffor an absolute URL (no base-URL prefixing);signRequestnow delegates to it.
Changed #
- Bumped the
synidependency to^0.3.0, whoseSyniCloudConfigreplaced the staticauthTokenwith the request-awareauthHeaders. Consumers building aSyniCloudConfigdirectly need to update to the new shape; consumers usingSynheart.configure()are unaffected. - The SDK-side default
SyniCloudConfig.authHeadersclosure lazily attachesX-Synheart-ProofviaSynheart.buildProofHeader— works whether device auth resolves through the DartDeviceAuthProvideror the core-runtime ABI registration path (sdkDeviceAuthAbi=true).
0.5.3 - 2026-05-21 #
Fixed #
- iOS: the native runtime can now start on the ONNX-backed tiers.
The podspec force-loads ONNX Runtime into the host app binary. The
runtime framework resolves
OrtGetApiBaseat load time, andonnxruntime-cships ONNX as a static archive — nothing in the host app references it directly, so the linker dead-stripped the whole archive and the runtime crashed on first use. Consumers no longer need any per-app linker configuration; depending onsynheart_coreis enough. iOS build configuration only — no Dart API change.
0.5.2 - 2026-05-20 #
Changed #
- Bumped the
synheart_behaviordependency to^0.4.0. The behavior SDK is now an event producer — a session summary may arrive with nobehavioralMetrics.BehaviorSessionResults.fromSummaryalready treats those metrics as optional and defaults each to 0 when absent.
0.5.1 - 2026-05-19 #
Changed #
- Scrubbed internal references from the public package surface — dartdoc comments, section header dividers, and barrel-file comments no longer cite internal identifiers or internal documentation paths. Comments and doc strings only; no code or API changes.
0.5.0 - 2026-05-19 #
Added #
- Cross-device baseline sync.
Synheart.syncCreateSpace,syncJoinSpace,syncGeneratePairing,syncStatus— pair two devices and replicate baselines in either direction with end-to-end encryption. - Offline export / import.
Synheart.baselineExportOffline(passphrase)produces an encrypted.srm.synheartbundle (6-word passphrase, never sent to any server).baselineImportOffline(passphrase, bytes)returns{imported, skipped, errors}. Lets users move baselines across devices without the cloud. BaselineLocalHydratorfacade —wireLocalHydrator(...)is the new entry point for hydrating baselines into the SDK from device-local sources.
Fixed #
- iOS native runtime loading. Replaced the relative-path framework
open with
DynamicLibrary.process(), which resolves symbols from the auto-loaded embedded framework. The previous form silently failed and surfaced only as a generic "Native runtime not loaded" warning. - Runtime initialization failures are now visible. When the native runtime rejects a configuration on iOS, the host now sees the actual reason instead of a silent fallback.
Changed (breaking) #
wireCloud(...)removed. Migrate towireLocalHydrator(...). The separate baseline cloud uploader is retired; baselines now ride the cross-device sync path.- iOS install model. Podspec moves to a vendored dynamic framework
installed via the synheart CLI (
synheart install runtime), rather than a static library bundled with the package. Consumer Podfile gets aprepare_commandsymlink pointing at the CLI-managed vendor dir.
Other #
- Logging hygiene: consent change events emit one structured line instead of an 8-line block; native bridge startup is silent on the happy path and warns only on real failures; baseline scoring no longer dumps the full engine input JSON.
Requires #
- The matching native runtime release (
synheart-core-runtimev0.10.0).
0.4.0 - 2026-05-16 #
Added #
Synheart.syni— gated Syni client surface with install lifecycle + chat + chatStream, delegating topackage:syni'sSyniAgent.SyniContextBuilder— projects this SDK's HSI (live state + stored session history) into the runtime's conditioning contract. HSI-version-agnostic; iterates whatever axes the runtime emitted.- Trivial-message context skip — for greetings / acks, ship only the persona prefix instead of full HSI + history (~30–50% prefill reduction on short messages).
SyniSpecPersonare-exported frompackage:syniso consumers canSyniSpecPersona.load('focus.coach.v1')for canonical prompts.Synheart.{closeOrphanSession, sweepOrphanSessions}— host-side orphan cleanup. Sweep on app start closesstate='active'sessions older than 6h that the runtime never finalized (force-kill, OS reclaim, sudden reboot).Synheart.configureSyniCloud(...)— injects cloud client config;Synheart.syni.hasCloud+ per-callSyniExecutionModeroutes between local and cloud.- Cold-start restore —
SyniAgent.restoreInstallIfReadychecks disk before download flow; consumers don't re-prompt for an install when the model is already cached.
Changed #
synidependency switched frompath: ../syni-flutterto^0.1.0(now published on pub.dev).SessionSummaryArtifactparser rewritten to match the runtime's actual wire format (nestedheaderblock,started_at_ms/ended_at_mskeys, nullable per-axis aggregates, structuredSessionAggregateskeyed by axis name).SessionRecordnow readsstarted_at_msfrom the FFI (was missing — surfaced as 1970-01-01 timestamps in downstream digests).stateandendedAtUtcexposed for the sweep's filter.
0.3.0 - 2026-05-14 #
Added #
Synheart.labReenqueueSession(String sessionJson)— replay a previously-finalized lab session payload through the cloud connector. Useful when the initial upload was dropped on a 4xx (typically a cloud schema mismatch — the runtime removes those rows from the in-memory upload queue peringest/hsi/connector.rs::deliver_lab_chunk). Host reads the persisted JSON from app-side storage and passes it back.Synheart.isLabReenqueueAvailable— feature-detect whether the linked runtime binary exports the symbol (engine v0.8.1+).LabReenqueueResultenum — mirrors the FFI return codes (queued,researchNotAllowed,cloudNotConfigured,parseError,invalidArgument,unsupported).CoreRuntimeBridge.labReenqueueSessioninstance method + companionisLabReenqueueAvailablegetter; new_LabReenqueueC/_LabReenqueueDarttypedefs inffi_bindings.dart. Lookup is optional so older runtime binaries (pre-v0.8.1) keep loading without errors.- Customer-facing data deletion API (
requestDataDeletion,getDataDeletion,listDataDeletions) andDataDeletion*models for GDPR Article 17 (landed via PR #40).
Runtime compatibility #
- Requires
synheart-core-runtimev0.8.1+ forlabReenqueueSessionto actually invoke the FFI. Older binaries returnLabReenqueueResult.unsupported.
0.2.0 - 2026-05-09 #
Added #
- HSI 1.3 envelope parsing in
HSIStateandHSIPayload. Producers emit the closed 5-axis domain set (physiological,kinematic,digital,cognitive,affective) with deterministic UUIDv5hsi_id. The SDK now parses the new shape and exposes the digital readings (focus_quality,interruption_pressure,interaction_mode) alongside the existing physiological / cognitive / affective fields. The 1.2 wire shape is still accepted as a fallback; consumers should not rely on that path long-term.
Fixed #
BehaviorModule._convertSynheartEventnow forwardsBehaviorEventType.app_switchto the runtime instead of dropping it via the default-arm. The runtime needsapp_switchto detect notification responses (an app switch shortly after a notification) and to anchor session boundaries between foreground events of different apps. Without this forward, the digital readings on the HSI 1.3 envelope (axes.digital[]—focus_quality,interruption_pressure,interaction_mode) were silent on iOS and Android.
0.1.1 - 2026-05-08 #
Changed #
- Bumped
synheart_authdep to^0.1.2. Picks up the Maven Centralai.synheart:synheart-auth:0.1.1upgrade (clock-skew auto-apply, register/rotate race fix, HTTP timeouts, §13 audit-log PII redaction).
0.1.0 - 2026-05-08 #
First public open-source release on pub.dev. The SDK is now a thin FFI shell over the native Synheart Runtime — storage, crypto, sync, consent, the artifact pipeline, the cloud connector, and SRM live in the runtime, and this package exposes them through a Dart surface.
The native runtime is license-gated and installed via the
Synheart CLI
(synheart install runtime); see the README.
This release consolidates the OSS-launch refactors that were tagged
internally as 0.0.3 and 0.0.4 but never reached pub.dev, plus the
post-tag breaking change to processVendorEvent.
Breaking #
Synheart.processVendorEvent(...)andWearModule.processVendorEvent(...)now returnFuture<CanonicalWearableEvent?>instead ofFuture<void>. The previous void return discarded the canonical event the vendor payload was mapped to. Mirrors the Swift and Kotlin counterparts.- Removed
CloudConfig.tenantId. Drop the argument —app_idis the only identifier the SDK sends. - Removed
CloudConfig.hmacSecret. Request signing uses the device key;authProvideris now optional. - Removed
InvalidTenantError. ConsentFormshape is flat (profile_id,biosignals,phone_context,behavior,consent_tier,allow_cloud,allow_research,allow_vendor_sync) to mirror the runtime. Hosts that previously builtcategories[] → channels[]structures must migrate to the flat form.- Consent type strings are snake_case at the runtime boundary
(
phone_context/cloud_upload/vendor_sync). Flutter still accepts camelCase on its public API. - Removed
ConsentCategoryandConsentChanneltypes — channel-level truth is owned by the runtime.
Added #
Synheart.rawRamenEvents— broadcastStream<RamenEvent>re-exported fromsynheart_wear, surfacing every RAMEN event with its capability-flavoreddeliveryHint.app_id/user_idfrom the activestartVendorSyncconfig are stamped onto each event soRamenEventDispatchercan drive REST pulls without extra plumbing.- Offline-first consent FFI surface:
consentConfigureCloud,consentGetEditableForm,consentSubmitForm,consentEffectiveState,consentStatus,consentNeedsTokenRefresh,consentClearStored. Local choice is persisted immediately; cloud sync is best-effort. - Typed consent form accessors:
consentGetEditableFormTyped()andconsentSubmitFormTyped({ required ConsentForm form, … }). - Consent coverage for
vendorSyncandresearchthroughhasConsent()/getConsentStatusMap()and the granular grant API. - Durable
data_dirfor the native runtime viapath_provider. Fixes SRM snapshots and the artifact SQLite landing instd::env::temp_dir()(cleaned up by the OS), which broke baseline persistence across app restarts.
Changed #
setStreamCallbackauto-route now skipsdelivery_hint == "ping"events. Their inlinepayload_jsonis empty by design (Garmin / Oura / Fitbit only send a notification), so apps must subscribe torawRamenEventsand useRamenEventDispatcherto fetch the full record before re-feeding the canonical pipeline.- README rewritten to match the actual public API.
- Dependencies on sibling Synheart SDKs are now hosted on pub.dev
(
synheart_wear ^0.4.0,synheart_session ^0.2.0,synheart_behavior ^0.3.0,synheart_auth ^0.1.1) instead of git refs.