Implement Phase 6R-AG native capture-route workflow

This commit is contained in:
axiomlogicnexus 2026-05-26 18:45:51 +02:00
parent 8ad3dfbc37
commit 9da661a974
13 changed files with 1559 additions and 25 deletions

View file

@ -958,6 +958,63 @@ namespace HyperTwistContractLibraryInternal
return Profile;
}
FHyperTwistSpeechNativeCaptureRouteWorkflowProfile
MakeDefaultSpeechNativeCaptureRouteWorkflowProfile(const FString& ProfileId)
{
FHyperTwistSpeechNativeCaptureRouteWorkflowProfile Profile;
Profile.NativeCaptureRouteWorkflowProfileId = ProfileId;
Profile.WorkflowKind = TEXT("first-party-native-capture-route-workflow");
Profile.OwnershipContractId = TEXT("native-capture-route-ownership-v1");
Profile.PreparationContractId = TEXT("native-capture-route-preparation-v1");
Profile.SessionReopenContractId = TEXT("native-capture-route-session-reopen-v1");
Profile.RouteInspectActionId = TEXT("inspect-native-capture-route");
Profile.PreparationRetryActionId = TEXT("retry-capture-route");
Profile.SessionReopenActionId = TEXT("reopen-speech-session");
Profile.PermissionDependencyActionId = TEXT("resolve-device-permission-workflow");
Profile.bSupportsRouteInspect = true;
Profile.bSupportsPreparationRetry = true;
Profile.bSupportsSessionReopen = true;
auto AddPanel = [&Profile](
const TCHAR* PanelId,
const TCHAR* PanelKind,
const TCHAR* AnchorId,
const bool bVisibleByDefault
)
{
FHyperTwistSpeechShellPanelLayout Panel;
Panel.PanelId = PanelId;
Panel.PanelKind = PanelKind;
Panel.AnchorId = AnchorId;
Panel.bVisibleByDefault = bVisibleByDefault;
Profile.Panels.Add(Panel);
};
auto AddAction = [&Profile](
const TCHAR* ActionId,
const TCHAR* InputBinding,
const TCHAR* SurfaceId
)
{
FHyperTwistSpeechShellActionBinding ActionBinding;
ActionBinding.ActionId = ActionId;
ActionBinding.InputBinding = InputBinding;
ActionBinding.SurfaceId = SurfaceId;
Profile.ActionBindings.Add(ActionBinding);
};
AddPanel(TEXT("capture-route-ownership-summary-card"), TEXT("capture-route-ownership-summary-card"), TEXT("top-left"), true);
AddPanel(TEXT("capture-route-preparation-card"), TEXT("capture-route-preparation-card"), TEXT("left-stack-below-summary"), true);
AddPanel(TEXT("capture-route-session-reopen-card"), TEXT("capture-route-session-reopen-card"), TEXT("right-stack-aligned-summary"), true);
AddAction(TEXT("inspect-native-capture-route"), TEXT("I"), TEXT("capture-route-ownership-summary-card"));
AddAction(TEXT("retry-capture-route"), TEXT("R"), TEXT("capture-route-preparation-card"));
AddAction(TEXT("reopen-speech-session"), TEXT("O"), TEXT("capture-route-session-reopen-card"));
AddAction(TEXT("resolve-device-permission-workflow"), TEXT("P"), TEXT("capture-route-ownership-summary-card"));
AddAction(TEXT("close-speech-session"), TEXT("Escape"), TEXT("session-root"));
return Profile;
}
FHyperTwistSpeechUsageCostDashboardShellProfile MakeDefaultSpeechUsageCostDashboardShellProfile(
const FString& ProfileId
)
@ -2527,6 +2584,12 @@ FHyperTwistSpeechSessionConfig UHyperTwistContractLibrary::MakeSampleSpeechSessi
HyperTwistContractLibraryInternal::MakeDefaultSpeechDevicePermissionWorkflowProfile(
Config.DevicePermissionWorkflowProfileId
);
Config.NativeCaptureRouteWorkflowProfileId =
TEXT("speech-native-capture-route-workflow-v1");
Config.NativeCaptureRouteWorkflowProfileDefinition =
HyperTwistContractLibraryInternal::MakeDefaultSpeechNativeCaptureRouteWorkflowProfile(
Config.NativeCaptureRouteWorkflowProfileId
);
Config.ProviderProfileId = TEXT("speech-provider/local-http-sidecar-profile-v1");
Config.ProviderProfileDefinition =
HyperTwistContractLibraryInternal::MakeLocalHttpSpeechProviderProfile(
@ -2707,6 +2770,10 @@ FHyperTwistSpeechServiceHealth UHyperTwistContractLibrary::MakeMockSpeechService
TEXT("devicePermissionWorkflow"),
TEXT("devicePermissionSettingsHandoff"),
TEXT("devicePermissionRecheck"),
TEXT("nativeCaptureRouteWorkflow"),
TEXT("captureRoutePreparationControl"),
TEXT("captureRouteOwnershipAssessment"),
TEXT("reopenSpeechSession"),
TEXT("streamingPreview"),
TEXT("manualTranscriptCommit"),
TEXT("batchedTranscribe"),

View file

@ -327,6 +327,10 @@ FHyperTwistSpeechServiceHealth UHyperTwistHttpSpeechClient::GetSpeechServiceHeal
TEXT("devicePermissionWorkflow"),
TEXT("devicePermissionSettingsHandoff"),
TEXT("devicePermissionRecheck"),
TEXT("nativeCaptureRouteWorkflow"),
TEXT("captureRoutePreparationControl"),
TEXT("captureRouteOwnershipAssessment"),
TEXT("reopenSpeechSession"),
TEXT("streamingPreview"),
TEXT("manualTranscriptCommit"),
TEXT("batchedTranscribe"),
@ -419,6 +423,10 @@ FHyperTwistSpeechServiceHealth UHyperTwistHttpSpeechClient::GetSpeechServiceHeal
TEXT("devicePermissionWorkflow"),
TEXT("devicePermissionSettingsHandoff"),
TEXT("devicePermissionRecheck"),
TEXT("nativeCaptureRouteWorkflow"),
TEXT("captureRoutePreparationControl"),
TEXT("captureRouteOwnershipAssessment"),
TEXT("reopenSpeechSession"),
TEXT("streamingPreview"),
TEXT("manualTranscriptCommit"),
TEXT("batchedTranscribe"),
@ -487,6 +495,10 @@ FHyperTwistSpeechServiceHealth UHyperTwistHttpSpeechClient::GetSpeechServiceHeal
Health.Capabilities.AddUnique(TEXT("devicePermissionWorkflow"));
Health.Capabilities.AddUnique(TEXT("devicePermissionSettingsHandoff"));
Health.Capabilities.AddUnique(TEXT("devicePermissionRecheck"));
Health.Capabilities.AddUnique(TEXT("nativeCaptureRouteWorkflow"));
Health.Capabilities.AddUnique(TEXT("captureRoutePreparationControl"));
Health.Capabilities.AddUnique(TEXT("captureRouteOwnershipAssessment"));
Health.Capabilities.AddUnique(TEXT("reopenSpeechSession"));
Health.Capabilities.AddUnique(TEXT("streamingPreview"));
Health.Capabilities.AddUnique(TEXT("manualTranscriptCommit"));
Health.SupportedOrchestrationProfiles.AddUnique(TEXT("python-batch-transcribe-v1"));

View file

@ -135,6 +135,10 @@ FHyperTwistSpeechServiceHealth UHyperTwistMockSpeechClient::GetSpeechServiceHeal
Health.Capabilities.AddUnique(TEXT("devicePermissionWorkflow"));
Health.Capabilities.AddUnique(TEXT("devicePermissionSettingsHandoff"));
Health.Capabilities.AddUnique(TEXT("devicePermissionRecheck"));
Health.Capabilities.AddUnique(TEXT("nativeCaptureRouteWorkflow"));
Health.Capabilities.AddUnique(TEXT("captureRoutePreparationControl"));
Health.Capabilities.AddUnique(TEXT("captureRouteOwnershipAssessment"));
Health.Capabilities.AddUnique(TEXT("reopenSpeechSession"));
Health.Capabilities.AddUnique(TEXT("streamingPreview"));
Health.Capabilities.AddUnique(TEXT("manualTranscriptCommit"));
Health.Capabilities.AddUnique(TEXT("batchedTranscribe"));

View file

@ -2921,6 +2921,11 @@ namespace HyperTwistTrainingSubsystemInternal
const FHyperTwistTrainingCompanionSpeechSessionState* PriorState
);
FHyperTwistSpeechNativeCaptureRouteWorkflowState BuildSpeechNativeCaptureRouteWorkflowState(
const FHyperTwistSpeechSessionConfig& SessionConfig,
const FHyperTwistTrainingCompanionSpeechSessionState* PriorState
);
FHyperTwistSpeechUsageCostDashboardShellState BuildSpeechUsageCostDashboardShellState(
const FHyperTwistSpeechSessionConfig& SessionConfig,
const FHyperTwistTrainingCompanionSpeechSessionState* PriorState
@ -3064,6 +3069,63 @@ namespace HyperTwistTrainingSubsystemInternal
return Profile;
}
FHyperTwistSpeechNativeCaptureRouteWorkflowProfile
BuildDefaultSpeechNativeCaptureRouteWorkflowProfile(const FString& ProfileId)
{
FHyperTwistSpeechNativeCaptureRouteWorkflowProfile Profile;
Profile.NativeCaptureRouteWorkflowProfileId = ProfileId;
Profile.WorkflowKind = TEXT("first-party-native-capture-route-workflow");
Profile.OwnershipContractId = TEXT("native-capture-route-ownership-v1");
Profile.PreparationContractId = TEXT("native-capture-route-preparation-v1");
Profile.SessionReopenContractId = TEXT("native-capture-route-session-reopen-v1");
Profile.RouteInspectActionId = TEXT("inspect-native-capture-route");
Profile.PreparationRetryActionId = TEXT("retry-capture-route");
Profile.SessionReopenActionId = TEXT("reopen-speech-session");
Profile.PermissionDependencyActionId = TEXT("resolve-device-permission-workflow");
Profile.bSupportsRouteInspect = true;
Profile.bSupportsPreparationRetry = true;
Profile.bSupportsSessionReopen = true;
auto AddPanel = [&Profile](
const TCHAR* PanelId,
const TCHAR* PanelKind,
const TCHAR* AnchorId,
const bool bVisibleByDefault
)
{
FHyperTwistSpeechShellPanelLayout Panel;
Panel.PanelId = PanelId;
Panel.PanelKind = PanelKind;
Panel.AnchorId = AnchorId;
Panel.bVisibleByDefault = bVisibleByDefault;
Profile.Panels.Add(Panel);
};
auto AddAction = [&Profile](
const TCHAR* ActionId,
const TCHAR* InputBinding,
const TCHAR* SurfaceId
)
{
FHyperTwistSpeechShellActionBinding ActionBinding;
ActionBinding.ActionId = ActionId;
ActionBinding.InputBinding = InputBinding;
ActionBinding.SurfaceId = SurfaceId;
Profile.ActionBindings.Add(ActionBinding);
};
AddPanel(TEXT("capture-route-ownership-summary-card"), TEXT("capture-route-ownership-summary-card"), TEXT("top-left"), true);
AddPanel(TEXT("capture-route-preparation-card"), TEXT("capture-route-preparation-card"), TEXT("left-stack-below-summary"), true);
AddPanel(TEXT("capture-route-session-reopen-card"), TEXT("capture-route-session-reopen-card"), TEXT("right-stack-aligned-summary"), true);
AddAction(TEXT("inspect-native-capture-route"), TEXT("I"), TEXT("capture-route-ownership-summary-card"));
AddAction(TEXT("retry-capture-route"), TEXT("R"), TEXT("capture-route-preparation-card"));
AddAction(TEXT("reopen-speech-session"), TEXT("O"), TEXT("capture-route-session-reopen-card"));
AddAction(TEXT("resolve-device-permission-workflow"), TEXT("P"), TEXT("capture-route-ownership-summary-card"));
AddAction(TEXT("close-speech-session"), TEXT("Escape"), TEXT("session-root"));
return Profile;
}
FHyperTwistSpeechUsageCostDashboardShellProfile BuildDefaultSpeechUsageCostDashboardShellProfile(
const FString& ProfileId
)
@ -4554,6 +4616,14 @@ namespace HyperTwistTrainingSubsystemInternal
Health.Capabilities.AddUnique(TEXT("devicePermissionRecheck"));
}
void ApplySpeechNativeCaptureRouteHealthDefaults(FHyperTwistSpeechServiceHealth& Health)
{
Health.Capabilities.AddUnique(TEXT("nativeCaptureRouteWorkflow"));
Health.Capabilities.AddUnique(TEXT("captureRoutePreparationControl"));
Health.Capabilities.AddUnique(TEXT("captureRouteOwnershipAssessment"));
Health.Capabilities.AddUnique(TEXT("reopenSpeechSession"));
}
bool IsSpeechPermissionError(const FString& ErrorText)
{
const FString NormalizedError = ErrorText.ToLower();
@ -4984,6 +5054,283 @@ bool IsSpeechPermissionRecheckError(const FString& ErrorText)
ApplySpeechDevicePermissionWorkflowPosture(Profile, SessionState, WorkflowState);
}
void AddSpeechNativeCaptureRouteWorkflowIssue(
FHyperTwistSpeechNativeCaptureRouteWorkflowState& WorkflowState,
const FHyperTwistSpeechNativeCaptureRouteWorkflowIssue& Issue
)
{
if (!Issue.IsStructurallyValid())
{
return;
}
WorkflowState.Issues.Add(Issue);
if (!WorkflowState.ActiveIssue.IsStructurallyValid())
{
WorkflowState.ActiveIssue = Issue;
}
}
void ApplySpeechNativeCaptureRouteWorkflowPosture(
const FHyperTwistSpeechNativeCaptureRouteWorkflowProfile& Profile,
const FHyperTwistTrainingCompanionSpeechSessionState& SessionState,
FHyperTwistSpeechNativeCaptureRouteWorkflowState& WorkflowState
)
{
const FHyperTwistSpeechMicrophoneShellState& ShellState = SessionState.MicrophoneShellState;
const FHyperTwistSpeechDevicePermissionWorkflowState& PermissionWorkflowState =
SessionState.DevicePermissionWorkflowState;
const FHyperTwistSpeechProviderRouteDecision RouteDecision =
SessionState.ServiceHealth.ProviderRouteDecision.IsStructurallyValid()
? SessionState.ServiceHealth.ProviderRouteDecision
: SessionState.SessionConfig.ProviderRouteDecision;
const FString RouteError = !SessionState.LastError.IsEmpty()
? SessionState.LastError
: (!ShellState.LastRouteError.IsEmpty()
? ShellState.LastRouteError
: (!SessionState.ServiceHealth.LastError.IsEmpty()
? SessionState.ServiceHealth.LastError
: RouteDecision.FailureReason));
WorkflowState.CaptureRouteStateId = !ShellState.CaptureRouteStateId.IsEmpty()
? ShellState.CaptureRouteStateId
: (!RouteDecision.RouteStateId.IsEmpty() ? RouteDecision.RouteStateId : TEXT("route-closed"));
WorkflowState.PermissionStateId =
PermissionWorkflowState.IsStructurallyValid() && !PermissionWorkflowState.PermissionStateId.IsEmpty()
? PermissionWorkflowState.PermissionStateId
: (!ShellState.PermissionStateId.IsEmpty()
? ShellState.PermissionStateId
: TEXT("permission-granted"));
WorkflowState.ActiveProviderProfileId =
RouteDecision.IsStructurallyValid() && !RouteDecision.SelectedProviderProfileId.IsEmpty()
? RouteDecision.SelectedProviderProfileId
: (!SessionState.SessionConfig.ProviderProfileId.IsEmpty()
? SessionState.SessionConfig.ProviderProfileId
: TEXT("speech-provider/local-http-sidecar-profile-v1"));
WorkflowState.ActiveServiceLaneId =
RouteDecision.IsStructurallyValid() && !RouteDecision.SelectedServiceLaneId.IsEmpty()
? RouteDecision.SelectedServiceLaneId
: (!SessionState.SessionConfig.OrchestrationProfile.ServiceLaneId.IsEmpty()
? SessionState.SessionConfig.OrchestrationProfile.ServiceLaneId
: TEXT("speech/python-service"));
WorkflowState.LatestSourceKind = TEXT("capture-route-ready");
WorkflowState.WorkflowStateId = TEXT("capture-route-workflow-cleared");
WorkflowState.StatusLine = SessionState.bSessionOpen
? TEXT("Native capture route owned for the active speech session.")
: TEXT("Open the speech session before native capture-route ownership begins.");
WorkflowState.DetailLine = SessionState.bSessionOpen
? TEXT("The first-party native capture-route workflow is clear for the bounded speech lane.")
: TEXT("The workflow stays first-party, but session-open control must happen before route ownership can be confirmed.");
WorkflowState.LastRouteError = RouteError;
WorkflowState.WorkflowEntryCount = 0;
WorkflowState.PreparationEntryCount = 0;
WorkflowState.RouteRetryEntryCount = 0;
WorkflowState.SessionReopenEntryCount = 0;
WorkflowState.PermissionDependencyEntryCount = 0;
WorkflowState.bRouteOwnershipConfirmed = SessionState.bSessionOpen;
WorkflowState.bWorkflowReady = SessionState.bSessionOpen;
WorkflowState.bPreparationRequired = false;
WorkflowState.bSessionOpenRequired = false;
WorkflowState.bPermissionWorkflowBlocked = false;
WorkflowState.bRouteInspectVisible = false;
WorkflowState.bPreparationRetryVisible = false;
WorkflowState.bSessionReopenVisible = false;
WorkflowState.Entries.Reset();
WorkflowState.Issues.Reset();
WorkflowState.ActiveIssue = FHyperTwistSpeechNativeCaptureRouteWorkflowIssue();
WorkflowState.ActiveIssueCount = 0;
if (!SessionState.bSessionOpen)
{
FHyperTwistSpeechNativeCaptureRouteWorkflowEntry Entry;
Entry.EntryId = TEXT("capture-route-session-open-required");
Entry.SourceKind = TEXT("session-open-required");
Entry.WorkflowStateId = TEXT("capture-route-session-open-required");
Entry.CaptureRouteStateId = WorkflowState.CaptureRouteStateId;
Entry.StatusLine = TEXT("Open the speech session before native capture-route ownership begins.");
Entry.DetailLine =
TEXT("The bounded first-party route owner is not active while the speech session is closed. Reopen the session before capture-route assessment continues.");
Entry.RecommendedActionId = Profile.SessionReopenActionId;
Entry.bRequiresSessionOpen = true;
FHyperTwistSpeechNativeCaptureRouteWorkflowIssue Issue;
Issue.IssueId = TEXT("capture-route-session-open-required");
Issue.IssueKind = TEXT("session-open-required");
Issue.StatusLine = Entry.StatusLine;
Issue.DetailLine = Entry.DetailLine;
Issue.RecommendedActionId = Entry.RecommendedActionId;
WorkflowState.Entries.Add(Entry);
WorkflowState.WorkflowEntryCount = 1;
WorkflowState.SessionReopenEntryCount = 1;
WorkflowState.WorkflowStateId = Entry.WorkflowStateId;
WorkflowState.LatestSourceKind = Entry.SourceKind;
WorkflowState.StatusLine = Entry.StatusLine;
WorkflowState.DetailLine = Entry.DetailLine;
WorkflowState.bRouteOwnershipConfirmed = false;
WorkflowState.bWorkflowReady = false;
WorkflowState.bSessionOpenRequired = true;
WorkflowState.bSessionReopenVisible = Profile.bSupportsSessionReopen;
AddSpeechNativeCaptureRouteWorkflowIssue(WorkflowState, Issue);
}
else if ((PermissionWorkflowState.IsStructurallyValid()
&& (!PermissionWorkflowState.bPermissionGranted
|| !PermissionWorkflowState.bPermissionWorkflowReady))
|| WorkflowState.PermissionStateId != TEXT("permission-granted"))
{
FHyperTwistSpeechNativeCaptureRouteWorkflowEntry Entry;
Entry.EntryId = TEXT("capture-route-permission-dependency");
Entry.SourceKind = TEXT("permission-workflow-dependency");
Entry.WorkflowStateId = TEXT("capture-route-awaiting-permission");
Entry.CaptureRouteStateId = TEXT("route-waiting-for-permission");
Entry.StatusLine =
TEXT("Resolve the device-permission workflow before capture-route ownership continues.");
Entry.DetailLine =
TEXT("The first-party capture-route owner is waiting on the bounded permission workflow. Clear permission review, request, or recheck posture before route preparation resumes.");
Entry.RecommendedActionId = Profile.PermissionDependencyActionId;
Entry.bRequiresPermissionWorkflow = true;
FHyperTwistSpeechNativeCaptureRouteWorkflowIssue Issue;
Issue.IssueId = TEXT("capture-route-permission-dependency");
Issue.IssueKind = TEXT("permission-workflow-dependency");
Issue.StatusLine = Entry.StatusLine;
Issue.DetailLine = Entry.DetailLine;
Issue.RecommendedActionId = Entry.RecommendedActionId;
WorkflowState.Entries.Add(Entry);
WorkflowState.WorkflowEntryCount = 1;
WorkflowState.PermissionDependencyEntryCount = 1;
WorkflowState.WorkflowStateId = Entry.WorkflowStateId;
WorkflowState.CaptureRouteStateId = Entry.CaptureRouteStateId;
WorkflowState.LatestSourceKind = Entry.SourceKind;
WorkflowState.StatusLine = Entry.StatusLine;
WorkflowState.DetailLine = Entry.DetailLine;
WorkflowState.bRouteOwnershipConfirmed = false;
WorkflowState.bWorkflowReady = false;
WorkflowState.bPermissionWorkflowBlocked = true;
AddSpeechNativeCaptureRouteWorkflowIssue(WorkflowState, Issue);
}
else if (WorkflowState.CaptureRouteStateId == TEXT("route-awaiting-endpoint")
|| RouteError == TEXT("service-base-url-missing"))
{
FHyperTwistSpeechNativeCaptureRouteWorkflowEntry Entry;
Entry.EntryId = TEXT("capture-route-preparation-required");
Entry.SourceKind = TEXT("capture-route-preparation-required");
Entry.WorkflowStateId = TEXT("capture-route-preparation-required");
Entry.CaptureRouteStateId = TEXT("route-awaiting-endpoint");
Entry.StatusLine = TEXT("Native capture route needs preparation before ownership can start.");
Entry.DetailLine = RouteError.IsEmpty()
? TEXT("Inspect the selected route, prepare the local sidecar or device path, then reopen the speech session once the bounded route is ready.")
: FString::Printf(
TEXT("The capture route reported '%s'. Inspect the selected route, prepare the local sidecar or device path, then reopen the speech session."),
*RouteError
);
Entry.RecommendedActionId = Profile.RouteInspectActionId;
Entry.bRequiresPreparation = true;
FHyperTwistSpeechNativeCaptureRouteWorkflowIssue Issue;
Issue.IssueId = TEXT("capture-route-preparation-required");
Issue.IssueKind = TEXT("capture-route-preparation-required");
Issue.StatusLine = Entry.StatusLine;
Issue.DetailLine = Entry.DetailLine;
Issue.RecommendedActionId = Entry.RecommendedActionId;
WorkflowState.Entries.Add(Entry);
WorkflowState.WorkflowEntryCount = 1;
WorkflowState.PreparationEntryCount = 1;
WorkflowState.WorkflowStateId = Entry.WorkflowStateId;
WorkflowState.CaptureRouteStateId = Entry.CaptureRouteStateId;
WorkflowState.LatestSourceKind = Entry.SourceKind;
WorkflowState.StatusLine = Entry.StatusLine;
WorkflowState.DetailLine = Entry.DetailLine;
WorkflowState.bRouteOwnershipConfirmed = false;
WorkflowState.bWorkflowReady = false;
WorkflowState.bPreparationRequired = true;
WorkflowState.bRouteInspectVisible = Profile.bSupportsRouteInspect;
AddSpeechNativeCaptureRouteWorkflowIssue(WorkflowState, Issue);
}
else if (WorkflowState.CaptureRouteStateId == TEXT("route-retry-required")
|| WorkflowState.CaptureRouteStateId == TEXT("route-health-degraded")
|| (RouteDecision.IsStructurallyValid() && !RouteDecision.bRouteReady)
|| (IsSpeechRouteError(RouteError) && RouteError != TEXT("no-active-training-run")))
{
FHyperTwistSpeechNativeCaptureRouteWorkflowEntry Entry;
Entry.EntryId = TEXT("capture-route-retry-required");
Entry.SourceKind = TEXT("capture-route-retry-required");
Entry.WorkflowStateId = TEXT("capture-route-retry-required");
Entry.CaptureRouteStateId = TEXT("route-retry-required");
Entry.StatusLine = TEXT("Native capture route must be rechecked before ownership resumes.");
Entry.DetailLine = RouteError.IsEmpty()
? TEXT("The bounded route owner cannot confirm capture readiness yet. Retry the capture route once the sidecar or device path reports healthy again.")
: FString::Printf(
TEXT("The capture route reported '%s'. Retry the route once the sidecar or device path reports healthy again."),
*RouteError
);
Entry.RecommendedActionId = Profile.PreparationRetryActionId;
Entry.bRequiresRouteRetry = true;
FHyperTwistSpeechNativeCaptureRouteWorkflowIssue Issue;
Issue.IssueId = TEXT("capture-route-retry-required");
Issue.IssueKind = TEXT("capture-route-retry-required");
Issue.StatusLine = Entry.StatusLine;
Issue.DetailLine = Entry.DetailLine;
Issue.RecommendedActionId = Entry.RecommendedActionId;
WorkflowState.Entries.Add(Entry);
WorkflowState.WorkflowEntryCount = 1;
WorkflowState.RouteRetryEntryCount = 1;
WorkflowState.WorkflowStateId = Entry.WorkflowStateId;
WorkflowState.CaptureRouteStateId = Entry.CaptureRouteStateId;
WorkflowState.LatestSourceKind = Entry.SourceKind;
WorkflowState.StatusLine = Entry.StatusLine;
WorkflowState.DetailLine = Entry.DetailLine;
WorkflowState.bRouteOwnershipConfirmed = false;
WorkflowState.bWorkflowReady = false;
WorkflowState.bPreparationRetryVisible = Profile.bSupportsPreparationRetry;
AddSpeechNativeCaptureRouteWorkflowIssue(WorkflowState, Issue);
}
WorkflowState.ActiveIssueCount = WorkflowState.Issues.Num();
}
void SynchronizeSpeechNativeCaptureRouteWorkflowState(
FHyperTwistTrainingCompanionSpeechSessionState& SessionState
)
{
FHyperTwistSpeechSessionConfig EffectiveConfig = SessionState.SessionConfig;
if (EffectiveConfig.NativeCaptureRouteWorkflowProfileId.IsEmpty())
{
EffectiveConfig.NativeCaptureRouteWorkflowProfileId =
TEXT("speech-native-capture-route-workflow-v1");
}
const FHyperTwistSpeechNativeCaptureRouteWorkflowProfile Profile =
EffectiveConfig.NativeCaptureRouteWorkflowProfileDefinition.IsStructurallyValid()
? EffectiveConfig.NativeCaptureRouteWorkflowProfileDefinition
: BuildDefaultSpeechNativeCaptureRouteWorkflowProfile(
!EffectiveConfig.NativeCaptureRouteWorkflowProfileId.IsEmpty()
? EffectiveConfig.NativeCaptureRouteWorkflowProfileId
: TEXT("speech-native-capture-route-workflow-v1")
);
if (!SessionState.NativeCaptureRouteWorkflowState.IsStructurallyValid())
{
SessionState.NativeCaptureRouteWorkflowState =
BuildSpeechNativeCaptureRouteWorkflowState(EffectiveConfig, &SessionState);
}
FHyperTwistSpeechNativeCaptureRouteWorkflowState& WorkflowState =
SessionState.NativeCaptureRouteWorkflowState;
WorkflowState.NativeCaptureRouteWorkflowProfileId =
Profile.NativeCaptureRouteWorkflowProfileId;
WorkflowState.AvailableActionIds.Reset();
for (const FHyperTwistSpeechShellActionBinding& ActionBinding : Profile.ActionBindings)
{
WorkflowState.AvailableActionIds.AddUnique(ActionBinding.ActionId);
}
ApplySpeechNativeCaptureRouteWorkflowPosture(Profile, SessionState, WorkflowState);
}
void AddSpeechUsageCostDashboardIssue(
FHyperTwistSpeechUsageCostDashboardShellState& ShellState,
const FHyperTwistSpeechUsageCostDashboardIssue& Issue
@ -6830,6 +7177,46 @@ bool IsSpeechPermissionRecheckError(const FString& ErrorText)
return WorkflowState;
}
FHyperTwistSpeechNativeCaptureRouteWorkflowState BuildSpeechNativeCaptureRouteWorkflowState(
const FHyperTwistSpeechSessionConfig& SessionConfig,
const FHyperTwistTrainingCompanionSpeechSessionState* PriorState
)
{
const FHyperTwistSpeechNativeCaptureRouteWorkflowProfile Profile =
SessionConfig.NativeCaptureRouteWorkflowProfileDefinition.IsStructurallyValid()
? SessionConfig.NativeCaptureRouteWorkflowProfileDefinition
: BuildDefaultSpeechNativeCaptureRouteWorkflowProfile(
!SessionConfig.NativeCaptureRouteWorkflowProfileId.IsEmpty()
? SessionConfig.NativeCaptureRouteWorkflowProfileId
: TEXT("speech-native-capture-route-workflow-v1")
);
FHyperTwistSpeechNativeCaptureRouteWorkflowState WorkflowState;
WorkflowState.NativeCaptureRouteWorkflowProfileId =
Profile.NativeCaptureRouteWorkflowProfileId;
for (const FHyperTwistSpeechShellActionBinding& ActionBinding : Profile.ActionBindings)
{
WorkflowState.AvailableActionIds.AddUnique(ActionBinding.ActionId);
}
if (PriorState != nullptr
&& PriorState->NativeCaptureRouteWorkflowState.IsStructurallyValid()
&& PriorState->NativeCaptureRouteWorkflowState.NativeCaptureRouteWorkflowProfileId
== Profile.NativeCaptureRouteWorkflowProfileId)
{
WorkflowState = PriorState->NativeCaptureRouteWorkflowState;
WorkflowState.NativeCaptureRouteWorkflowProfileId =
Profile.NativeCaptureRouteWorkflowProfileId;
WorkflowState.AvailableActionIds.Reset();
for (const FHyperTwistSpeechShellActionBinding& ActionBinding : Profile.ActionBindings)
{
WorkflowState.AvailableActionIds.AddUnique(ActionBinding.ActionId);
}
}
return WorkflowState;
}
FHyperTwistSpeechUsageCostDashboardShellState BuildSpeechUsageCostDashboardShellState(
const FHyperTwistSpeechSessionConfig& SessionConfig,
const FHyperTwistTrainingCompanionSpeechSessionState* PriorState
@ -12469,6 +12856,11 @@ bool UHyperTwistTrainingSubsystem::OpenActiveCompanionSpeechSession(FString& Out
SessionConfig,
nullptr
);
ActiveCompanionSpeechSessionState.NativeCaptureRouteWorkflowState =
HyperTwistTrainingSubsystemInternal::BuildSpeechNativeCaptureRouteWorkflowState(
SessionConfig,
nullptr
);
ActiveCompanionSpeechSessionState.UsageCostHistoryExportShellState =
HyperTwistTrainingSubsystemInternal::BuildSpeechUsageCostHistoryExportShellState(
SessionConfig,
@ -12563,6 +12955,9 @@ FHyperTwistSpeechTranscriptResult UHyperTwistTrainingSubsystem::SubmitActiveComp
HyperTwistTrainingSubsystemInternal::SynchronizeSpeechDevicePermissionWorkflowState(
ActiveCompanionSpeechSessionState
);
HyperTwistTrainingSubsystemInternal::SynchronizeSpeechNativeCaptureRouteWorkflowState(
ActiveCompanionSpeechSessionState
);
HyperTwistTrainingSubsystemInternal::SynchronizeSpeechUsageCostDashboardState(
ActiveCompanionSpeechSessionState
);
@ -12752,6 +13147,9 @@ FHyperTwistSpeechTranscriptResult UHyperTwistTrainingSubsystem::SubmitActiveComp
HyperTwistTrainingSubsystemInternal::SynchronizeSpeechDevicePermissionWorkflowState(
ActiveCompanionSpeechSessionState
);
HyperTwistTrainingSubsystemInternal::SynchronizeSpeechNativeCaptureRouteWorkflowState(
ActiveCompanionSpeechSessionState
);
HyperTwistTrainingSubsystemInternal::SynchronizeSpeechUsageCostDashboardState(
ActiveCompanionSpeechSessionState
);
@ -13278,6 +13676,18 @@ FHyperTwistSpeechSessionConfig UHyperTwistTrainingSubsystem::BuildActiveCompanio
const bool bUseMockClient = CompanionSpeechClientKind.Equals(TEXT("mock"), ESearchCase::IgnoreCase);
if (!HasActiveRun())
{
if (SessionConfig.NativeCaptureRouteWorkflowProfileId.IsEmpty())
{
SessionConfig.NativeCaptureRouteWorkflowProfileId =
TEXT("speech-native-capture-route-workflow-v1");
}
if (!SessionConfig.NativeCaptureRouteWorkflowProfileDefinition.IsStructurallyValid())
{
SessionConfig.NativeCaptureRouteWorkflowProfileDefinition =
HyperTwistTrainingSubsystemInternal::BuildDefaultSpeechNativeCaptureRouteWorkflowProfile(
SessionConfig.NativeCaptureRouteWorkflowProfileId
);
}
HyperTwistTrainingSubsystemInternal::ApplySpeechPayloadCustodyDefaults(SessionConfig);
HyperTwistTrainingSubsystemInternal::ApplySpeechProviderProfileDefaults(
SessionConfig,
@ -13375,6 +13785,32 @@ FHyperTwistSpeechSessionConfig UHyperTwistTrainingSubsystem::BuildActiveCompanio
SessionConfig.DevicePermissionWorkflowProfileDefinition.DevicePermissionWorkflowProfileId =
SessionConfig.DevicePermissionWorkflowProfileId;
}
if (SessionConfig.NativeCaptureRouteWorkflowProfileId.IsEmpty()
&& SessionConfig.NativeCaptureRouteWorkflowProfileDefinition.IsStructurallyValid())
{
SessionConfig.NativeCaptureRouteWorkflowProfileId =
SessionConfig.NativeCaptureRouteWorkflowProfileDefinition
.NativeCaptureRouteWorkflowProfileId;
}
if (SessionConfig.NativeCaptureRouteWorkflowProfileId.IsEmpty())
{
SessionConfig.NativeCaptureRouteWorkflowProfileId =
TEXT("speech-native-capture-route-workflow-v1");
}
if (!SessionConfig.NativeCaptureRouteWorkflowProfileDefinition.IsStructurallyValid())
{
SessionConfig.NativeCaptureRouteWorkflowProfileDefinition =
HyperTwistTrainingSubsystemInternal::BuildDefaultSpeechNativeCaptureRouteWorkflowProfile(
SessionConfig.NativeCaptureRouteWorkflowProfileId
);
}
if (SessionConfig.NativeCaptureRouteWorkflowProfileDefinition
.NativeCaptureRouteWorkflowProfileId.IsEmpty())
{
SessionConfig.NativeCaptureRouteWorkflowProfileDefinition
.NativeCaptureRouteWorkflowProfileId =
SessionConfig.NativeCaptureRouteWorkflowProfileId;
}
SessionConfig.bEnableVad = true;
if (!SessionConfig.VadPolicy.IsStructurallyValid())
{
@ -13532,12 +13968,18 @@ void UHyperTwistTrainingSubsystem::RefreshCompanionSpeechServiceHealth()
HyperTwistTrainingSubsystemInternal::ApplySpeechPayloadCustodyHealthDefaults(
ActiveCompanionSpeechSessionState.ServiceHealth
);
HyperTwistTrainingSubsystemInternal::ApplySpeechNativeCaptureRouteHealthDefaults(
ActiveCompanionSpeechSessionState.ServiceHealth
);
HyperTwistTrainingSubsystemInternal::SynchronizeSpeechMicrophoneShellState(
ActiveCompanionSpeechSessionState
);
HyperTwistTrainingSubsystemInternal::SynchronizeSpeechDevicePermissionWorkflowState(
ActiveCompanionSpeechSessionState
);
HyperTwistTrainingSubsystemInternal::SynchronizeSpeechNativeCaptureRouteWorkflowState(
ActiveCompanionSpeechSessionState
);
HyperTwistTrainingSubsystemInternal::SynchronizeSpeechUsageCostDashboardState(
ActiveCompanionSpeechSessionState
);
@ -13575,12 +14017,18 @@ void UHyperTwistTrainingSubsystem::RefreshCompanionSpeechServiceHealth()
HyperTwistTrainingSubsystemInternal::ApplySpeechPayloadCustodyHealthDefaults(
ActiveCompanionSpeechSessionState.ServiceHealth
);
HyperTwistTrainingSubsystemInternal::ApplySpeechNativeCaptureRouteHealthDefaults(
ActiveCompanionSpeechSessionState.ServiceHealth
);
HyperTwistTrainingSubsystemInternal::SynchronizeSpeechMicrophoneShellState(
ActiveCompanionSpeechSessionState
);
HyperTwistTrainingSubsystemInternal::SynchronizeSpeechDevicePermissionWorkflowState(
ActiveCompanionSpeechSessionState
);
HyperTwistTrainingSubsystemInternal::SynchronizeSpeechNativeCaptureRouteWorkflowState(
ActiveCompanionSpeechSessionState
);
HyperTwistTrainingSubsystemInternal::SynchronizeSpeechUsageCostDashboardState(
ActiveCompanionSpeechSessionState
);

View file

@ -2097,6 +2097,331 @@ struct FHyperTwistSpeechDevicePermissionWorkflowState
}
};
USTRUCT(BlueprintType)
struct FHyperTwistSpeechNativeCaptureRouteWorkflowProfile
{
GENERATED_BODY()
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
FString NativeCaptureRouteWorkflowProfileId;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
FString WorkflowKind = TEXT("first-party-native-capture-route-workflow");
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
FString OwnershipContractId = TEXT("native-capture-route-ownership-v1");
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
FString PreparationContractId = TEXT("native-capture-route-preparation-v1");
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
FString SessionReopenContractId = TEXT("native-capture-route-session-reopen-v1");
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
FString RouteInspectActionId = TEXT("inspect-native-capture-route");
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
FString PreparationRetryActionId = TEXT("retry-capture-route");
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
FString SessionReopenActionId = TEXT("reopen-speech-session");
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
FString PermissionDependencyActionId = TEXT("resolve-device-permission-workflow");
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
bool bSupportsRouteInspect = true;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
bool bSupportsPreparationRetry = true;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
bool bSupportsSessionReopen = true;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
TArray<FHyperTwistSpeechShellPanelLayout> Panels;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
TArray<FHyperTwistSpeechShellActionBinding> ActionBindings;
bool IsStructurallyValid() const
{
if (NativeCaptureRouteWorkflowProfileId.IsEmpty()
|| WorkflowKind.IsEmpty()
|| OwnershipContractId.IsEmpty()
|| PreparationContractId.IsEmpty()
|| SessionReopenContractId.IsEmpty()
|| RouteInspectActionId.IsEmpty()
|| PreparationRetryActionId.IsEmpty()
|| SessionReopenActionId.IsEmpty()
|| PermissionDependencyActionId.IsEmpty()
|| Panels.Num() <= 0
|| ActionBindings.Num() <= 0)
{
return false;
}
for (const FHyperTwistSpeechShellPanelLayout& Panel : Panels)
{
if (!Panel.IsStructurallyValid())
{
return false;
}
}
for (const FHyperTwistSpeechShellActionBinding& ActionBinding : ActionBindings)
{
if (!ActionBinding.IsStructurallyValid())
{
return false;
}
}
return true;
}
};
USTRUCT(BlueprintType)
struct FHyperTwistSpeechNativeCaptureRouteWorkflowEntry
{
GENERATED_BODY()
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
FString EntryId;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
FString SourceKind;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
FString WorkflowStateId;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
FString CaptureRouteStateId;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
FString StatusLine;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
FString DetailLine;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
FString RecommendedActionId;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
bool bRequiresSessionOpen = false;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
bool bRequiresPermissionWorkflow = false;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
bool bRequiresPreparation = false;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
bool bRequiresRouteRetry = false;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
bool bOwnershipConfirmed = false;
bool IsStructurallyValid() const
{
return !EntryId.IsEmpty()
&& !SourceKind.IsEmpty()
&& !WorkflowStateId.IsEmpty()
&& !CaptureRouteStateId.IsEmpty()
&& !StatusLine.IsEmpty()
&& !DetailLine.IsEmpty()
&& !RecommendedActionId.IsEmpty();
}
};
USTRUCT(BlueprintType)
struct FHyperTwistSpeechNativeCaptureRouteWorkflowIssue
{
GENERATED_BODY()
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
FString IssueId;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
FString IssueKind;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
FString StatusLine;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
FString DetailLine;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
FString RecommendedActionId;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
bool bBlocksCaptureStart = true;
bool IsStructurallyValid() const
{
return !IssueId.IsEmpty()
&& !IssueKind.IsEmpty()
&& !StatusLine.IsEmpty()
&& !DetailLine.IsEmpty()
&& !RecommendedActionId.IsEmpty();
}
};
USTRUCT(BlueprintType)
struct FHyperTwistSpeechNativeCaptureRouteWorkflowState
{
GENERATED_BODY()
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
FString NativeCaptureRouteWorkflowProfileId;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
FString WorkflowStateId = TEXT("capture-route-workflow-cleared");
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
FString CaptureRouteStateId = TEXT("route-ready");
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
FString PermissionStateId = TEXT("permission-granted");
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
FString ActiveProviderProfileId = TEXT("speech-provider/local-http-sidecar-profile-v1");
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
FString ActiveServiceLaneId = TEXT("speech/python-service");
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
FString LatestSourceKind = TEXT("capture-route-ready");
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
FString StatusLine = TEXT("Native capture route already owned for the bounded speech session.");
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
FString DetailLine =
TEXT("The first-party native capture-route workflow is clear for the active speech lane.");
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
FString LastRouteError;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
int32 WorkflowEntryCount = 0;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
int32 PreparationEntryCount = 0;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
int32 RouteRetryEntryCount = 0;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
int32 SessionReopenEntryCount = 0;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
int32 PermissionDependencyEntryCount = 0;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
bool bRouteOwnershipConfirmed = true;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
bool bWorkflowReady = true;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
bool bPreparationRequired = false;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
bool bSessionOpenRequired = false;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
bool bPermissionWorkflowBlocked = false;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
bool bRouteInspectVisible = false;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
bool bPreparationRetryVisible = false;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
bool bSessionReopenVisible = false;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
int32 ActiveIssueCount = 0;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
FHyperTwistSpeechNativeCaptureRouteWorkflowIssue ActiveIssue;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
TArray<FHyperTwistSpeechNativeCaptureRouteWorkflowEntry> Entries;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
TArray<FHyperTwistSpeechNativeCaptureRouteWorkflowIssue> Issues;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
TArray<FString> AvailableActionIds;
bool IsStructurallyValid() const
{
if (NativeCaptureRouteWorkflowProfileId.IsEmpty()
|| WorkflowStateId.IsEmpty()
|| CaptureRouteStateId.IsEmpty()
|| PermissionStateId.IsEmpty()
|| ActiveProviderProfileId.IsEmpty()
|| ActiveServiceLaneId.IsEmpty()
|| LatestSourceKind.IsEmpty()
|| StatusLine.IsEmpty()
|| DetailLine.IsEmpty()
|| WorkflowEntryCount < 0
|| PreparationEntryCount < 0
|| RouteRetryEntryCount < 0
|| SessionReopenEntryCount < 0
|| PermissionDependencyEntryCount < 0
|| ActiveIssueCount < 0
|| AvailableActionIds.Num() <= 0)
{
return false;
}
if (WorkflowEntryCount != Entries.Num()
|| ActiveIssueCount != Issues.Num()
|| PreparationEntryCount > WorkflowEntryCount
|| RouteRetryEntryCount > WorkflowEntryCount
|| SessionReopenEntryCount > WorkflowEntryCount
|| PermissionDependencyEntryCount > WorkflowEntryCount)
{
return false;
}
if (ActiveIssueCount > 0 && !ActiveIssue.IsStructurallyValid())
{
return false;
}
for (const FHyperTwistSpeechNativeCaptureRouteWorkflowEntry& Entry : Entries)
{
if (!Entry.IsStructurallyValid())
{
return false;
}
}
for (const FHyperTwistSpeechNativeCaptureRouteWorkflowIssue& Issue : Issues)
{
if (!Issue.IsStructurallyValid())
{
return false;
}
}
for (const FString& ActionId : AvailableActionIds)
{
if (ActionId.IsEmpty())
{
return false;
}
}
return true;
}
};
USTRUCT(BlueprintType)
struct FHyperTwistSpeechModelPayloadDescriptor
{
@ -4770,6 +5095,14 @@ struct FHyperTwistSpeechSessionConfig
FHyperTwistSpeechDevicePermissionWorkflowProfile
DevicePermissionWorkflowProfileDefinition;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
FString NativeCaptureRouteWorkflowProfileId =
TEXT("speech-native-capture-route-workflow-v1");
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
FHyperTwistSpeechNativeCaptureRouteWorkflowProfile
NativeCaptureRouteWorkflowProfileDefinition;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
FString ProviderProfileId = TEXT("speech-provider/local-http-sidecar-profile-v1");
@ -4878,6 +5211,8 @@ struct FHyperTwistSpeechSessionConfig
|| !MicrophoneShellProfileDefinition.IsStructurallyValid()
|| DevicePermissionWorkflowProfileId.IsEmpty()
|| !DevicePermissionWorkflowProfileDefinition.IsStructurallyValid()
|| NativeCaptureRouteWorkflowProfileId.IsEmpty()
|| !NativeCaptureRouteWorkflowProfileDefinition.IsStructurallyValid()
|| ProviderProfileId.IsEmpty()
|| !ProviderProfileDefinition.IsStructurallyValid()
|| ByokCustodyProfileId.IsEmpty()

View file

@ -1943,6 +1943,9 @@ struct FHyperTwistTrainingCompanionSpeechSessionState
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
FHyperTwistSpeechDevicePermissionWorkflowState DevicePermissionWorkflowState;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
FHyperTwistSpeechNativeCaptureRouteWorkflowState NativeCaptureRouteWorkflowState;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
FHyperTwistSpeechUsageCostDashboardShellState UsageCostDashboardShellState;

View file

@ -0,0 +1,348 @@
// Copyright HyperTwist, Inc. All Rights Reserved.
#include "Misc/AutomationTest.h"
#include "Engine/GameInstance.h"
#include "HyperTwistBootstrap/HyperTwistContractLibrary.h"
#include "HyperTwistRecognition/HyperTwistSpeechClient.h"
#include "HyperTwistTraining/HyperTwistTrainingSubsystem.h"
#include "UObject/UnrealType.h"
#if WITH_AUTOMATION_TESTS
namespace HyperTwistWhisperCppPhase6RAGTestInternal
{
FHyperTwistTrainingDeck MakeSpeechDeck()
{
FHyperTwistTrainingDeck Deck;
Deck.DeckId = TEXT("phase6r-ag/whispercpp-native-capture-route-workflow");
Deck.Title = TEXT("Phase 6R-AG Whisper Native Capture Route Workflow");
Deck.DeliveryModes = {
EHyperTwistTrainingDeliveryMode::CoachReviewed
};
FHyperTwistTrainingCase TrainingCase;
TrainingCase.CaseId = TEXT("phase6r-ag-case");
TrainingCase.PuzzleId = TEXT("cube/3x3x3");
TrainingCase.PromptKind = EHyperTwistTrainingPromptKind::Sequence;
TrainingCase.PromptLabel = TEXT("Phase 6R-AG Coach Speech");
TrainingCase.AllowedDeliveryModes = {
EHyperTwistTrainingDeliveryMode::CoachReviewed
};
Deck.Cases = {TrainingCase};
return Deck;
}
void ForceSpeechClientKind(UHyperTwistTrainingSubsystem* TrainingSubsystem, const FString& ClientKind)
{
if (TrainingSubsystem == nullptr)
{
return;
}
if (FStrProperty* SpeechClientKindProperty = FindFProperty<FStrProperty>(
UHyperTwistTrainingSubsystem::StaticClass(),
TEXT("CompanionSpeechClientKind")
))
{
SpeechClientKindProperty->SetPropertyValue_InContainer(TrainingSubsystem, ClientKind);
}
}
UHyperTwistTrainingSubsystem* MakeSpeechSubsystem(const FString& SessionId, const FString& ClientKind)
{
UGameInstance* GameInstance = NewObject<UGameInstance>(GetTransientPackage());
if (GameInstance == nullptr)
{
return nullptr;
}
UHyperTwistTrainingSubsystem* TrainingSubsystem = NewObject<UHyperTwistTrainingSubsystem>(GameInstance);
if (TrainingSubsystem == nullptr)
{
return nullptr;
}
ForceSpeechClientKind(TrainingSubsystem, ClientKind);
const FHyperTwistTrainingRunState RunState = TrainingSubsystem->StartTrainingRunFromDeck(
MakeSpeechDeck(),
TEXT("phase6r-ag-user"),
SessionId,
EHyperTwistTrainingDeliveryMode::CoachReviewed
);
return RunState.IsStructurallyValid() ? TrainingSubsystem : nullptr;
}
FHyperTwistTrainingCompanionSpeechSessionState* ResolveActiveSpeechSessionState(
UHyperTwistTrainingSubsystem* TrainingSubsystem
)
{
if (TrainingSubsystem == nullptr)
{
return nullptr;
}
if (FStructProperty* SessionStateProperty = FindFProperty<FStructProperty>(
UHyperTwistTrainingSubsystem::StaticClass(),
TEXT("ActiveCompanionSpeechSessionState")
))
{
return SessionStateProperty->ContainerPtrToValuePtr<FHyperTwistTrainingCompanionSpeechSessionState>(
TrainingSubsystem
);
}
return nullptr;
}
void ForcePermissionError(UHyperTwistTrainingSubsystem* TrainingSubsystem, const FString& ErrorText)
{
if (FHyperTwistTrainingCompanionSpeechSessionState* SessionState =
ResolveActiveSpeechSessionState(TrainingSubsystem))
{
SessionState->LastError = ErrorText;
SessionState->ServiceHealth.LastError = ErrorText;
}
}
UHyperTwistHttpSpeechClient* ResolveHttpSpeechClient(UHyperTwistTrainingSubsystem* TrainingSubsystem)
{
if (TrainingSubsystem == nullptr)
{
return nullptr;
}
FString CloseError;
TrainingSubsystem->CloseActiveCompanionSpeechSession(CloseError);
if (FObjectPropertyBase* ClientProperty = FindFProperty<FObjectPropertyBase>(
UHyperTwistTrainingSubsystem::StaticClass(),
TEXT("ActiveCompanionSpeechClientObject")
))
{
return Cast<UHyperTwistHttpSpeechClient>(
ClientProperty->GetObjectPropertyValue_InContainer(TrainingSubsystem)
);
}
return nullptr;
}
void ForcePreparationState(
UHyperTwistTrainingSubsystem* TrainingSubsystem,
const FString& SessionId
)
{
if (FHyperTwistTrainingCompanionSpeechSessionState* SessionState =
ResolveActiveSpeechSessionState(TrainingSubsystem))
{
SessionState->bSessionOpen = true;
SessionState->ActiveSessionId = SessionId;
SessionState->OpenedAtUtc = FDateTime::UtcNow().ToIso8601();
SessionState->LastUpdatedAtUtc = SessionState->OpenedAtUtc;
SessionState->SessionConfig = UHyperTwistContractLibrary::MakeSampleSpeechSessionConfig();
SessionState->SessionConfig.SessionId = SessionId;
SessionState->LastError = TEXT("service-base-url-missing");
}
}
}
IMPLEMENT_SIMPLE_AUTOMATION_TEST(
FHyperTwistWhisperCppPhase6RAGNativeCaptureRouteWorkflowSessionConfigTest,
"HyperTwist.Permissive.WhisperCpp.Phase6R.AG.NativeCaptureRouteWorkflowSessionConfig",
EAutomationTestFlags::EditorContext | EAutomationTestFlags::EngineFilter
)
bool FHyperTwistWhisperCppPhase6RAGNativeCaptureRouteWorkflowSessionConfigTest::RunTest(
const FString& Parameters
)
{
UHyperTwistTrainingSubsystem* TrainingSubsystem =
HyperTwistWhisperCppPhase6RAGTestInternal::MakeSpeechSubsystem(
TEXT("phase6r-ag-config-session"),
TEXT("mock")
);
TestNotNull(TEXT("The speech subsystem must be constructed for Phase 6R-AG."), TrainingSubsystem);
if (TrainingSubsystem == nullptr)
{
return false;
}
FString OpenError;
TestTrue(TEXT("The native capture-route workflow route must open a speech session."), TrainingSubsystem->OpenActiveCompanionSpeechSession(OpenError));
TestTrue(TEXT("Opening the speech session must not report an error."), OpenError.IsEmpty());
const FHyperTwistTrainingCompanionSpeechSessionState SessionState =
TrainingSubsystem->GetActiveCompanionSpeechSessionState();
const FHyperTwistSpeechNativeCaptureRouteWorkflowProfile& Profile =
SessionState.SessionConfig.NativeCaptureRouteWorkflowProfileDefinition;
TestTrue(TEXT("The native capture-route workflow route must expose a structurally valid profile."), Profile.IsStructurallyValid());
TestEqual(TEXT("The workflow profile must preserve the first-party profile id."), Profile.NativeCaptureRouteWorkflowProfileId, TEXT("speech-native-capture-route-workflow-v1"));
TestEqual(TEXT("The workflow profile must preserve the workflow kind."), Profile.WorkflowKind, TEXT("first-party-native-capture-route-workflow"));
TestEqual(TEXT("The workflow profile must preserve the ownership contract id."), Profile.OwnershipContractId, TEXT("native-capture-route-ownership-v1"));
TestEqual(TEXT("The workflow profile must preserve the preparation contract id."), Profile.PreparationContractId, TEXT("native-capture-route-preparation-v1"));
TestEqual(TEXT("The workflow profile must preserve the reopen contract id."), Profile.SessionReopenContractId, TEXT("native-capture-route-session-reopen-v1"));
TestEqual(TEXT("The workflow profile must preserve the route inspect action id."), Profile.RouteInspectActionId, TEXT("inspect-native-capture-route"));
TestEqual(TEXT("The workflow profile must preserve the retry action id."), Profile.PreparationRetryActionId, TEXT("retry-capture-route"));
TestEqual(TEXT("The workflow profile must preserve the reopen action id."), Profile.SessionReopenActionId, TEXT("reopen-speech-session"));
TestEqual(TEXT("The workflow profile must preserve the permission dependency action id."), Profile.PermissionDependencyActionId, TEXT("resolve-device-permission-workflow"));
TestTrue(TEXT("The speech service health must expose native capture-route workflow capability."), SessionState.ServiceHealth.Capabilities.Contains(TEXT("nativeCaptureRouteWorkflow")));
TestTrue(TEXT("The speech service health must expose capture-route preparation control capability."), SessionState.ServiceHealth.Capabilities.Contains(TEXT("captureRoutePreparationControl")));
TestTrue(TEXT("The speech service health must expose capture-route ownership assessment capability."), SessionState.ServiceHealth.Capabilities.Contains(TEXT("captureRouteOwnershipAssessment")));
TestTrue(TEXT("The speech service health must expose speech-session reopen capability."), SessionState.ServiceHealth.Capabilities.Contains(TEXT("reopenSpeechSession")));
return true;
}
IMPLEMENT_SIMPLE_AUTOMATION_TEST(
FHyperTwistWhisperCppPhase6RAGNativeCaptureRouteWorkflowClearedStateTest,
"HyperTwist.Permissive.WhisperCpp.Phase6R.AG.NativeCaptureRouteWorkflowClearedState",
EAutomationTestFlags::EditorContext | EAutomationTestFlags::EngineFilter
)
bool FHyperTwistWhisperCppPhase6RAGNativeCaptureRouteWorkflowClearedStateTest::RunTest(
const FString& Parameters
)
{
UHyperTwistTrainingSubsystem* TrainingSubsystem =
HyperTwistWhisperCppPhase6RAGTestInternal::MakeSpeechSubsystem(
TEXT("phase6r-ag-cleared-session"),
TEXT("mock")
);
TestNotNull(TEXT("The speech subsystem must be constructed for Phase 6R-AG."), TrainingSubsystem);
if (TrainingSubsystem == nullptr)
{
return false;
}
FString OpenError;
TestTrue(TEXT("The native capture-route workflow route must open a speech session."), TrainingSubsystem->OpenActiveCompanionSpeechSession(OpenError));
TestTrue(TEXT("Opening the speech session must not report an error."), OpenError.IsEmpty());
const FHyperTwistTrainingCompanionSpeechSessionState SessionState =
TrainingSubsystem->GetActiveCompanionSpeechSessionState();
const FHyperTwistSpeechNativeCaptureRouteWorkflowState& WorkflowState =
SessionState.NativeCaptureRouteWorkflowState;
TestTrue(TEXT("The cleared native capture-route workflow state must be structurally valid."), WorkflowState.IsStructurallyValid());
TestEqual(TEXT("The cleared workflow must preserve the cleared workflow state id."), WorkflowState.WorkflowStateId, TEXT("capture-route-workflow-cleared"));
TestEqual(TEXT("The cleared workflow must preserve route-ready posture."), WorkflowState.CaptureRouteStateId, TEXT("route-ready"));
TestEqual(TEXT("The cleared workflow must preserve permission-granted posture."), WorkflowState.PermissionStateId, TEXT("permission-granted"));
TestEqual(TEXT("The cleared workflow must preserve the ready source kind."), WorkflowState.LatestSourceKind, TEXT("capture-route-ready"));
TestTrue(TEXT("The cleared workflow must confirm route ownership."), WorkflowState.bRouteOwnershipConfirmed);
TestTrue(TEXT("The cleared workflow must preserve workflow-ready posture."), WorkflowState.bWorkflowReady);
TestFalse(TEXT("The cleared workflow must not require preparation."), WorkflowState.bPreparationRequired);
TestFalse(TEXT("The cleared workflow must not require session reopen."), WorkflowState.bSessionOpenRequired);
TestFalse(TEXT("The cleared workflow must not block on permission workflow."), WorkflowState.bPermissionWorkflowBlocked);
TestEqual(TEXT("The cleared workflow must not surface active issues."), WorkflowState.ActiveIssueCount, 0);
return true;
}
IMPLEMENT_SIMPLE_AUTOMATION_TEST(
FHyperTwistWhisperCppPhase6RAGNativeCaptureRouteWorkflowPermissionDependencyTest,
"HyperTwist.Permissive.WhisperCpp.Phase6R.AG.NativeCaptureRouteWorkflowPermissionDependency",
EAutomationTestFlags::EditorContext | EAutomationTestFlags::EngineFilter
)
bool FHyperTwistWhisperCppPhase6RAGNativeCaptureRouteWorkflowPermissionDependencyTest::RunTest(
const FString& Parameters
)
{
UHyperTwistTrainingSubsystem* TrainingSubsystem =
HyperTwistWhisperCppPhase6RAGTestInternal::MakeSpeechSubsystem(
TEXT("phase6r-ag-permission-session"),
TEXT("mock")
);
TestNotNull(TEXT("The speech subsystem must be constructed for Phase 6R-AG."), TrainingSubsystem);
if (TrainingSubsystem == nullptr)
{
return false;
}
FString OpenError;
TestTrue(TEXT("The native capture-route workflow route must open a speech session."), TrainingSubsystem->OpenActiveCompanionSpeechSession(OpenError));
TestTrue(TEXT("Opening the speech session must not report an error."), OpenError.IsEmpty());
HyperTwistWhisperCppPhase6RAGTestInternal::ForcePermissionError(
TrainingSubsystem,
TEXT("microphone-permission-required")
);
TrainingSubsystem->RefreshCompanionSpeechServiceHealthForAutomation();
const FHyperTwistTrainingCompanionSpeechSessionState SessionState =
TrainingSubsystem->GetActiveCompanionSpeechSessionState();
const FHyperTwistSpeechNativeCaptureRouteWorkflowState& WorkflowState =
SessionState.NativeCaptureRouteWorkflowState;
TestTrue(TEXT("The permission-dependent native capture-route workflow state must remain structurally valid."), WorkflowState.IsStructurallyValid());
TestEqual(TEXT("The permission-dependent workflow must preserve the waiting workflow state id."), WorkflowState.WorkflowStateId, TEXT("capture-route-awaiting-permission"));
TestEqual(TEXT("The permission-dependent workflow must preserve the waiting capture-route posture."), WorkflowState.CaptureRouteStateId, TEXT("route-waiting-for-permission"));
TestEqual(TEXT("The permission-dependent workflow must preserve permission-required posture."), WorkflowState.PermissionStateId, TEXT("permission-required"));
TestEqual(TEXT("The permission-dependent workflow must preserve the permission-dependency source kind."), WorkflowState.LatestSourceKind, TEXT("permission-workflow-dependency"));
TestFalse(TEXT("The permission-dependent workflow must clear ownership confirmation."), WorkflowState.bRouteOwnershipConfirmed);
TestFalse(TEXT("The permission-dependent workflow must block workflow-ready posture."), WorkflowState.bWorkflowReady);
TestTrue(TEXT("The permission-dependent workflow must block on the permission workflow."), WorkflowState.bPermissionWorkflowBlocked);
TestEqual(TEXT("The permission-dependent workflow must retain one workflow entry."), WorkflowState.WorkflowEntryCount, 1);
TestEqual(TEXT("The permission-dependent workflow must retain one permission-dependency entry."), WorkflowState.PermissionDependencyEntryCount, 1);
TestEqual(TEXT("The permission-dependent workflow must classify the active issue correctly."), WorkflowState.ActiveIssue.IssueKind, TEXT("permission-workflow-dependency"));
TestEqual(TEXT("The permission-dependent workflow must recommend the bounded permission workflow action."), WorkflowState.ActiveIssue.RecommendedActionId, TEXT("resolve-device-permission-workflow"));
return true;
}
IMPLEMENT_SIMPLE_AUTOMATION_TEST(
FHyperTwistWhisperCppPhase6RAGNativeCaptureRouteWorkflowPreparationStateTest,
"HyperTwist.Permissive.WhisperCpp.Phase6R.AG.NativeCaptureRouteWorkflowPreparationState",
EAutomationTestFlags::EditorContext | EAutomationTestFlags::EngineFilter
)
bool FHyperTwistWhisperCppPhase6RAGNativeCaptureRouteWorkflowPreparationStateTest::RunTest(
const FString& Parameters
)
{
UHyperTwistTrainingSubsystem* TrainingSubsystem =
HyperTwistWhisperCppPhase6RAGTestInternal::MakeSpeechSubsystem(
TEXT("phase6r-ag-preparation-session"),
TEXT("http-sidecar")
);
TestNotNull(TEXT("The speech subsystem must be constructed for Phase 6R-AG."), TrainingSubsystem);
if (TrainingSubsystem == nullptr)
{
return false;
}
UHyperTwistHttpSpeechClient* HttpSpeechClient =
HyperTwistWhisperCppPhase6RAGTestInternal::ResolveHttpSpeechClient(TrainingSubsystem);
TestNotNull(TEXT("The provider-backed speech client must be available for the preparation workflow test."), HttpSpeechClient);
if (HttpSpeechClient == nullptr)
{
return false;
}
HttpSpeechClient->ServiceBaseUrl.Reset();
HyperTwistWhisperCppPhase6RAGTestInternal::ForcePreparationState(
TrainingSubsystem,
TEXT("phase6r-ag-preparation-session")
);
TrainingSubsystem->RefreshCompanionSpeechServiceHealthForAutomation();
const FHyperTwistTrainingCompanionSpeechSessionState SessionState =
TrainingSubsystem->GetActiveCompanionSpeechSessionState();
const FHyperTwistSpeechNativeCaptureRouteWorkflowState& WorkflowState =
SessionState.NativeCaptureRouteWorkflowState;
TestTrue(TEXT("The preparation-required native capture-route workflow state must remain structurally valid."), WorkflowState.IsStructurallyValid());
TestEqual(TEXT("The preparation-required workflow must preserve the preparation workflow state id."), WorkflowState.WorkflowStateId, TEXT("capture-route-preparation-required"));
TestEqual(TEXT("The preparation-required workflow must preserve the awaiting-endpoint route posture."), WorkflowState.CaptureRouteStateId, TEXT("route-awaiting-endpoint"));
TestEqual(TEXT("The preparation-required workflow must preserve the preparation source kind."), WorkflowState.LatestSourceKind, TEXT("capture-route-preparation-required"));
TestFalse(TEXT("The preparation-required workflow must clear ownership confirmation."), WorkflowState.bRouteOwnershipConfirmed);
TestFalse(TEXT("The preparation-required workflow must block workflow-ready posture."), WorkflowState.bWorkflowReady);
TestTrue(TEXT("The preparation-required workflow must report preparation required."), WorkflowState.bPreparationRequired);
TestTrue(TEXT("The preparation-required workflow must surface route inspection."), WorkflowState.bRouteInspectVisible);
TestEqual(TEXT("The preparation-required workflow must retain one workflow entry."), WorkflowState.WorkflowEntryCount, 1);
TestEqual(TEXT("The preparation-required workflow must retain one preparation entry."), WorkflowState.PreparationEntryCount, 1);
TestEqual(TEXT("The preparation-required workflow must classify the active issue correctly."), WorkflowState.ActiveIssue.IssueKind, TEXT("capture-route-preparation-required"));
TestEqual(TEXT("The preparation-required workflow must carry the missing-endpoint route error."), WorkflowState.LastRouteError, TEXT("service-base-url-missing"));
return true;
}
#endif

View file

@ -215,9 +215,14 @@ Status update on `2026-05-21`:
pass is now consumed
- the bounded first-party `Phase 6R-AF` real device-permission workflow packet is now landed in
current code
- the current next bounded move is a source-backed `Phase 6R-AG` first-party native
capture-route ownership and workflow preparation/control pass, not a new restrictive packet by
default
- the generic source-backed `Phase 6R-AG` first-party native capture-route ownership and
workflow preparation/control pass is now consumed
- the bounded first-party `Phase 6R-AG` native capture-route ownership and workflow
preparation/control packet is now landed in current code
- the current next bounded move is not another restrictive packet by default; if a new
speech-adjacent first-party packet is justified, keep actual OS permission-grant execution,
actual payment execution, provider-portal ownership, and actual payload shipping separately
deferred
- use the repo-row README census, the portfolio standing refresh backfill, and the `2R-A`
ownership contract for the current queue after that correction
@ -366,8 +371,8 @@ Current practical interpretation:
- the landed `PostHog/posthog` boundary-sensitive widening packet now lives in `docs/HYPERTWIST_PHASE_4R_PACKET_4R_D_POSTHOG_CONTROL_PLANE_IMPLEMENTATION_2026-05-13.md`
- the landed `screenpipe/screenpipe` boundary-sensitive widening packet now lives in `docs/HYPERTWIST_PHASE_4R_PACKET_4R_E_SCREENPIPE_CAPTURE_HISTORY_IMPLEMENTATION_2026-05-13.md`
- the landed `remotion-dev/remotion` boundary-sensitive widening packet now lives in `docs/HYPERTWIST_PHASE_4R_PACKET_4R_F_REMOTION_MEDIA_EXPORT_IMPLEMENTATION_2026-05-13.md`
- the next bounded move is a source-backed `Phase 6R-AG` first-party native capture-route
ownership and workflow preparation/control pass
- the bounded first-party `Phase 6R-AG` native capture-route ownership and workflow
preparation/control packet is now landed in current code
Companion docs:
@ -1647,10 +1652,16 @@ Approved working posture:
workflow metadata above the existing transcript-session, shell, permission/readiness,
payload-custody, provider-custody, provider-routing, usage/cost, history/export,
receipt-review, settlement, and exception/handoff seams
- the next bounded first-party implementation slice is now landed as native capture-route
ownership and workflow preparation/control metadata above the existing transcript-session,
shell, permission/readiness, permission-workflow, payload-custody, provider-custody,
provider-routing, usage/cost, history/export, receipt-review, settlement, and
exception/handoff seams
- the top-level provider/session contract remains first-party HyperTwist-owned and
provider-neutral; this row does not own that lane
- keep native capture-route ownership, actual payment execution, provider-portal ownership,
actual payload shipping, and future voice-asset review separate from the code-license judgment
- keep unrestricted low-level audio-device takeover, actual payment execution,
provider-portal ownership, actual payload shipping, and future voice-asset review separate from
the code-license judgment
- treat `SYSTRAN/faster-whisper` as the landed complementary Python-orchestration donor rather
than widening `whisper.cpp` into the runtime core

View file

@ -0,0 +1,164 @@
# HyperTwist Phase 6R-AG first-party native capture-route ownership and workflow preparation/control implementation packet
Created on `2026-05-26`
## Status
- first-party HyperTwist packet
- bounded `Phase 6R-AG` implementation slice
## Purpose
This packet lands the next bounded first-party slice above the landed speech
session, microphone-shell, permission/readiness shell, permission-workflow,
payload-custody, provider-custody, provider-routing, normalized usage/cost
event, operator-facing usage/cost dashboard, provider usage/cost
history/export, provider receipt-review, provider billing-settlement, and
provider settlement-exception seams.
The landed slice is:
- first-party native capture-route ownership and workflow preparation/control
It is not:
- an unrestricted low-level audio-device takeover packet
- an actual OS permission-grant execution packet
- an actual payment execution packet
- a provider-portal ownership packet
- an actual payload-shipping packet
- a broad assistant-platform packet
## Current authority basis
This implementation packet stands on:
- `docs/ops/HYPERTWIST_PROVIDER_NEUTRALITY_AND_BYOK_DOCTRINE_2026-05-21.md`
- `docs/arch/HYPERTWIST_PHASE6R_AG_FIRST_PARTY_NATIVE_CAPTURE_ROUTE_OWNERSHIP_AND_WORKFLOW_PREPARATION_CONTROL_PREPARATION_PACKET_2026-05-26.md`
- `docs/arch/HYPERTWIST_PHASE6R_AF_FIRST_PARTY_REAL_DEVICE_PERMISSION_WORKFLOW_IMPLEMENTATION_PACKET_2026-05-25.md`
- `docs/arch/HYPERTWIST_PHASE6R_V_WHISPER_CPP_DEVICE_PERMISSION_AND_CAPTURE_ROUTE_READINESS_SHELL_IMPLEMENTATION_PACKET_2026-05-24.md`
The preserved owners do not change:
- first-party HyperTwist remains the top-level provider/session owner
- first-party HyperTwist remains the usage/cost governance owner
- first-party HyperTwist remains the bounded native capture-route workflow
owner for this slice
- `ggml-org/whisper.cpp` remains bounded to offline STT donor seams
- `SYSTRAN/faster-whisper` remains bounded to complementary Python
orchestration/service-lane seams
## Landed scope
The current code now owns a bounded native capture-route workflow seam
through:
- retained recognition contract types for:
- `FHyperTwistSpeechNativeCaptureRouteWorkflowProfile`
- `FHyperTwistSpeechNativeCaptureRouteWorkflowEntry`
- `FHyperTwistSpeechNativeCaptureRouteWorkflowIssue`
- `FHyperTwistSpeechNativeCaptureRouteWorkflowState`
- expanded `FHyperTwistSpeechSessionConfig`
- expanded `FHyperTwistTrainingCompanionSpeechSessionState`
- contract-library defaults for:
- native capture-route workflow profile ids and definitions
- workflow panel ids and action bindings
- service-health capability exposure
- active companion session-config, service-health, and derived native
capture-route workflow composition in:
- `UHyperTwistTrainingSubsystem`
- direct speech-client health capability exposure in:
- `UHyperTwistHttpSpeechClient`
- `UHyperTwistMockSpeechClient`
- focused automation coverage in:
- `HyperTwistWhisperCppPhase6RAGNativeCaptureRouteWorkflowContractTest.cpp`
## Why this is still intentionally bounded
This packet lands the bounded native capture-route ownership assessment and
workflow preparation/control family only.
Still deferred:
- unrestricted low-level audio-device takeover beyond the landed bounded
workflow/control posture
- actual OS permission-grant execution ownership
- actual payment execution or provider-portal ownership
- actual downloadable model/payload shipping
- broad assistant-platform scope
## Validation
Build validation:
- `UnrealHyperTwistEditor Win64 Development`
Focused automation validation:
- `HyperTwist.Permissive.WhisperCpp.Phase6R.AG` `4/4`
Regression automation validation:
- `HyperTwist.Permissive.WhisperCpp.Phase6R.AF` `3/3`
- `HyperTwist.Permissive.WhisperCpp.Phase6R.AE` `3/3`
- `HyperTwist.Permissive.WhisperCpp.Phase6R.V` `3/3`
Non-blocking warnings stayed limited to the existing Unreal headless/editor
noise, the pre-existing plugin dependency warning on `UnrealMCP`, the headless
CEF/web-browser warning, and the known `http:/vision/health` plus
`http:/speech/health` hostname-resolution warnings in provider-backed
automation.
## Queue effect
This packet consumes the current `Phase 6R-AG` implementation slice.
`ggml-org/whisper.cpp` remains only partially incorporated:
- landed now:
- speech transcript session boundary
- live microphone shell boundary
- device-permission and capture-route readiness shell posture
- first-party real device-permission workflow boundary
- first-party native capture-route ownership assessment and workflow
preparation/control boundary for:
- session-open-required posture
- permission-workflow dependency posture
- preparation-required posture
- route-retry posture
- bounded capability exposure for:
- `nativeCaptureRouteWorkflow`
- `captureRoutePreparationControl`
- `captureRouteOwnershipAssessment`
- `reopenSpeechSession`
- downloadable model and payload custody boundary
- first-party provider-profile and BYOK custody boundary
- first-party provider routing and workflow-policy boundary
- first-party normalized usage/cost event-model boundary
- first-party operator-facing provider usage/cost dashboard shell boundary
- first-party provider usage/cost history/export shell boundary
- first-party provider receipt review and posted-charge inspection shell
boundary
- first-party provider billing settlement and invoice reconciliation shell
boundary
- first-party provider settlement exception and external-portal handoff shell
boundary
- still deferred:
- unrestricted low-level audio-device takeover beyond the landed bounded
workflow/control posture
- actual OS permission-grant execution ownership
- actual payment execution or provider-portal ownership
- actual downloadable model/payload shipping
- broad assistant-platform scope
The next clean move is not another restrictive packet by default.
If a follow-on speech-adjacent packet is later justified, keep these guards
visible:
- prove a narrower remaining native capture-route ownership gap before widening
into actual OS permission-grant execution
- keep actual payment execution and provider-portal ownership separate from any
such packet
- keep actual payload shipping separate from usage/cost governance unless a
narrower first-party gap is proven first

View file

@ -0,0 +1,132 @@
# HyperTwist Phase 6R-AG first-party native capture-route ownership and workflow preparation/control preparation packet
Created on `2026-05-26`
## Status
- first-party HyperTwist packet
- source-backed `Phase 6R-AG` preparation/control slice
## Purpose
This packet scopes the next bounded first-party slice above the landed speech
session, microphone-shell, permission/readiness shell, permission-workflow,
payload-custody, provider-custody, provider-routing, normalized usage/cost
event, operator-facing usage/cost dashboard, provider usage/cost
history/export, provider receipt-review, provider billing-settlement, and
provider settlement-exception seams.
The granted slice is:
- first-party native capture-route ownership and workflow preparation/control
only
It is not:
- an unrestricted low-level audio-device takeover packet
- an actual OS permission-grant execution packet
- an actual payment execution packet
- a provider-portal ownership packet
- an actual payload-shipping packet
- a broad assistant-platform packet
## Current authority basis
This control pass stands on:
- `docs/ops/HYPERTWIST_PROVIDER_NEUTRALITY_AND_BYOK_DOCTRINE_2026-05-21.md`
- `docs/arch/HYPERTWIST_PHASE6R_AF_FIRST_PARTY_REAL_DEVICE_PERMISSION_WORKFLOW_IMPLEMENTATION_PACKET_2026-05-25.md`
- `docs/arch/HYPERTWIST_PHASE6R_V_WHISPER_CPP_DEVICE_PERMISSION_AND_CAPTURE_ROUTE_READINESS_SHELL_IMPLEMENTATION_PACKET_2026-05-24.md`
- `docs/v6_5_deep_manual_pack/HyperTwist/ROADMAP.md`
The preserved owners do not change here:
- first-party HyperTwist remains the top-level provider/session owner
- first-party HyperTwist remains the usage/cost governance owner
- first-party HyperTwist remains the bounded native capture-route workflow
owner for this slice
- `ggml-org/whisper.cpp` remains bounded to offline STT donor seams
- `SYSTRAN/faster-whisper` remains bounded to complementary Python
orchestration/service-lane seams
## Granted family
The bounded `Phase 6R-AG` slice may land:
1. first-party native capture-route workflow profile ids and definitions
2. derived native capture-route workflow entry, issue, and state record shapes
3. session-config and companion-session-state expansion for those shapes
4. bounded capability exposure for:
- `nativeCaptureRouteWorkflow`
- `captureRoutePreparationControl`
- `captureRouteOwnershipAssessment`
- `reopenSpeechSession`
5. focused automation for:
- cleared ownership posture
- permission-workflow dependency posture
- preparation-required posture
- route/session reopen posture
The packet must stay out of:
- unrestricted low-level audio-device takeover
- actual OS permission-grant execution ownership
- actual payment execution
- provider-portal ownership
- actual payload download or redistribution ownership
## Proposed implementation shape
Land the narrower first-party boundary through:
- retained recognition contract types for:
- native capture-route workflow profile
- native capture-route workflow entry
- native capture-route workflow issue
- native capture-route workflow state
- expanded speech session config
- expanded companion speech session state
- contract-library defaults for:
- native capture-route workflow profile ids and definitions
- action bindings and panel ids
- service-health capability exposure
- training-subsystem composition for:
- session-open-required posture
- permission-workflow dependency posture
- preparation-required posture
- capture-route retry posture
- explicit separation between bounded workflow control and unrestricted
low-level audio-device takeover
- explicit separation between bounded workflow control and actual OS
permission-grant execution
- direct speech-client health capability exposure for:
- `nativeCaptureRouteWorkflow`
- `captureRoutePreparationControl`
- `captureRouteOwnershipAssessment`
- `reopenSpeechSession`
## Validation target
Validate with:
- Unreal build for `UnrealHyperTwistEditor Win64 Development`
- focused automation:
- `HyperTwist.Permissive.WhisperCpp.Phase6R.AG`
- regressions:
- `HyperTwist.Permissive.WhisperCpp.Phase6R.AF`
- `HyperTwist.Permissive.WhisperCpp.Phase6R.AE`
- `HyperTwist.Permissive.WhisperCpp.Phase6R.V`
## Queue effect
If this packet lands cleanly, the speech-adjacent queue should still stay
narrow.
If another first-party speech packet is later justified, it must prove a
narrower remaining native capture-route ownership gap before widening into:
- actual OS permission-grant execution
- actual payment execution
- provider-portal ownership
- actual payload shipping
- broad assistant-platform scope

View file

@ -192,15 +192,17 @@ The next bounded move is now:
control pass is now consumed
38. the bounded first-party `Phase 6R-AF` real device-permission workflow packet is now
landed in current code
39. the next bounded move is a source-backed `Phase 6R-AG` first-party native
capture-route ownership and workflow preparation/control pass
40. keep the speech-lane guard visible:
39. the generic source-backed `Phase 6R-AG` first-party native capture-route ownership and
workflow preparation/control pass is now consumed
40. the bounded first-party `Phase 6R-AG` native capture-route ownership and workflow
preparation/control packet is now landed in current code
41. keep the speech-lane guard visible:
- keep code-license judgments separate from model, voice, and payload-license review
41. keep the provider-neutral speech-lane guard visible:
42. keep the provider-neutral speech-lane guard visible:
- first-party HyperTwist owns the top-level provider/session contract
- `whisper.cpp` and `faster-whisper` may win narrower donor slices without inheriting that
lane, the provider routing/policy lane, or the usage/cost governance lane
40. keep the `MagicTile` guard visible:
43. keep the `MagicTile` guard visible:
- keep broad non-Euclidean interaction or WinForms/OpenTK host-shell ownership closed by
default unless a narrower first-party gap is proven above the landed macro-remapping seam

View file

@ -131,7 +131,8 @@ Do not collapse those three tiers into one undifferentiated "features" voice.
| First-party provider receipt review and posted-charge inspection shell | Implemented now | landed first-party `Phase 6R-AC` | First-party receipt-review shell profiles, derived receipt-review entries, charge-pending and variance issue posture, and bounded inspection capability exposure are now live above the landed provider usage/cost history/export seam. |
| First-party provider billing settlement and invoice reconciliation shell | Implemented now | landed first-party `Phase 6R-AD` | First-party settlement shell profiles, derived settlement entries, invoice-reconciliation posture, settlement-readiness issue state, and bounded settlement capability exposure are now live above the landed provider receipt-review seam. |
| First-party provider settlement exception and external-portal handoff shell | Implemented now | landed first-party `Phase 6R-AE` | First-party settlement-exception shell profiles, derived exception entries, reference-only external-portal handoff posture, and bounded exception capability exposure are now live above the landed provider settlement seam. |
| First-party real device-permission workflow | Implemented now | landed first-party `Phase 6R-AF` | First-party permission-workflow profiles, request/settings/recheck posture, and bounded workflow capability exposure are now live above the landed permission/readiness shell seam. |
| First-party real device-permission workflow | Implemented now | landed first-party `Phase 6R-AF` | First-party permission-workflow profiles, request/settings/recheck posture, and bounded workflow capability exposure are now live above the landed permission/readiness shell seam. The bounded first-party native capture-route workflow/control layer now lives in landed `Phase 6R-AG`. |
| First-party native capture-route ownership and workflow preparation/control | Implemented now | landed first-party `Phase 6R-AG` | First-party native capture-route ownership assessment, preparation/retry/reopen posture, and bounded workflow capability exposure are now live above the landed permission-workflow seam. Actual OS permission-grant execution, payment execution, provider-portal ownership, and payload shipping remain deferred. |
| Analytics/reporting surfaces | Implemented now | landed analytics/reporting packets | Reporting is real, but bounded to accepted retained slices. |
| Browser/spatial/media adjunct surfaces | Implemented now | landed `three.js`, `react-three-fiber`, `xr`, `model-viewer`, `remotion` packets | These are implemented bounded families, not proof of unlimited browser-shell parity. |
@ -211,7 +212,8 @@ repo.
| Speech transcript session boundary | Implemented now | landed `whisper.cpp` packet | Current bounded STT truth. |
| Live microphone capture shell | Implemented now | landed `whisper.cpp` `Phase 6R-U` packet | Current bounded microphone shell truth above the landed transcript-session and orchestration seams. |
| Device-permission and capture-route readiness shell | Implemented now | landed `whisper.cpp` `Phase 6R-V` packet | Current bounded permission/readiness shell posture, route fallback issue objects, and explicit retry/request semantics above the landed microphone shell seam. |
| First-party real device-permission workflow | Implemented now | landed first-party `Phase 6R-AF` packet | Current bounded permission-workflow profile ids/definitions, request/settings/recheck posture, and workflow capability exposure are live above the landed permission/readiness shell seam. Native capture-route ownership remains deferred. |
| First-party real device-permission workflow | Implemented now | landed first-party `Phase 6R-AF` packet | Current bounded permission-workflow profile ids/definitions, request/settings/recheck posture, and workflow capability exposure are live above the landed permission/readiness shell seam. The bounded first-party native capture-route workflow/control layer now lives in landed `Phase 6R-AG`. |
| First-party native capture-route ownership and workflow preparation/control | Implemented now | landed first-party `Phase 6R-AG` packet | Current bounded native capture-route ownership assessment, preparation/retry/reopen posture, and workflow capability exposure are live above the landed permission-workflow seam. Actual OS permission-grant execution, payment execution, provider-portal ownership, and payload shipping remain deferred. |
| Downloadable model and payload custody boundary | Implemented now | landed `whisper.cpp` `Phase 6R-W` packet | Current bounded custody profile, required/supported payload descriptors, and health-surface integrity metadata above the landed transcript-session and shell seams. |
| Provider-backed speech session health and transcript envelopes | Implemented now | first-party speech client surfaces | Current first-party speech seam. |
| Python transcription-service orchestration profile | Implemented now | landed `faster-whisper` packet | Current bounded Python STT orchestration truth above the existing speech session boundary. |

View file

@ -191,9 +191,14 @@ Canonical discovery surfaces for roadmap interpretation:
pass is now consumed
- the bounded first-party `Phase 6R-AF` real device-permission workflow packet is now landed in
current code
- the current next bounded move is a source-backed `Phase 6R-AG` first-party native
capture-route ownership and workflow preparation/control pass, while keeping actual payment
execution, provider-portal ownership, and actual payload shipping separately deferred
- the generic source-backed `Phase 6R-AG` first-party native capture-route ownership and
workflow preparation/control pass is now consumed
- the bounded first-party `Phase 6R-AG` native capture-route ownership and workflow
preparation/control packet is now landed in current code
- the current next bounded move is not another restrictive packet by default; if a new
speech-adjacent first-party packet is justified, keep actual OS permission-grant execution,
actual payment execution, provider-portal ownership, and actual payload shipping separately
deferred
- the repo-row implementation queue is now live from that `Phase 6R-A` entry point rather than
waiting on another first-party packet
@ -280,8 +285,8 @@ Current routing truth:
- `brianpeiris/RiftSketch`
- the v6.3 CSV label `not_live_reference_or_discard_candidate` is not the final state by itself; read the closed `0R-E` packet for the retained-versus-discarded split
The next bounded move is a source-backed `Phase 6R-AG` first-party native
capture-route ownership and workflow preparation/control pass.
The bounded first-party `Phase 6R-AG` native capture-route ownership and
workflow preparation/control pass is now landed in current code.
Queue interpretation after that control pass:
@ -411,8 +416,8 @@ Queue interpretation after that control pass:
- provider settlement-exception review
- still deferred:
- actual payment execution or provider-portal ownership
- real device-permission workflow
- native capture-route ownership beyond bounded shell posture
- actual OS permission-grant execution ownership
- native capture-route ownership beyond the landed bounded workflow/control posture
- actual downloadable model or payload shipping
- broad assistant-platform scope
- `SYSTRAN/faster-whisper` is now landed as a partially incorporated row rather than a generic
@ -458,15 +463,16 @@ Queue interpretation after that control pass:
- viseme / gesture runtime integration
- broad assistant-platform scope
- the next queue shape is now:
- first-party native capture-route ownership and workflow assessment above the
- bounded first-party native capture-route ownership and workflow assessment/control above the
landed speech session, shell, permission/readiness, permission-workflow,
payload-custody, provider-custody, provider-routing, normalized usage/cost,
operator-facing dashboard, history/export, receipt-review, settlement, and
exception/handoff seams
- the next bounded move should stay narrow:
- a source-backed `Phase 6R-AG` control pass before any widening into actual
payment execution, provider-portal ownership, actual payload shipping,
broad voice-output ownership, or broad assistant-platform scope
- if a new speech-adjacent first-party packet is justified, prove a narrower remaining native
capture-route ownership gap before any widening into actual OS permission-grant execution,
actual payment execution, provider-portal ownership, actual payload shipping, broad
voice-output ownership, or broad assistant-platform scope
- keep the speech-input / voice sidecar legal sequencing guard visible:
- keep code-license judgments separate from model, voice, and payload-license review
- keep the provider-neutral speech-lane guard visible: