Implement Phase 6R-AF device permission workflow
This commit is contained in:
parent
a7e69c7f4d
commit
8ad3dfbc37
15 changed files with 1370 additions and 36 deletions
|
|
@ -903,6 +903,61 @@ namespace HyperTwistContractLibraryInternal
|
|||
return Profile;
|
||||
}
|
||||
|
||||
FHyperTwistSpeechDevicePermissionWorkflowProfile
|
||||
MakeDefaultSpeechDevicePermissionWorkflowProfile(const FString& ProfileId)
|
||||
{
|
||||
FHyperTwistSpeechDevicePermissionWorkflowProfile Profile;
|
||||
Profile.DevicePermissionWorkflowProfileId = ProfileId;
|
||||
Profile.WorkflowKind = TEXT("operator-device-permission-workflow");
|
||||
Profile.PermissionContractId = TEXT("device-permission-workflow-v1");
|
||||
Profile.SettingsHandoffContractId = TEXT("device-permission-settings-handoff-v1");
|
||||
Profile.PermissionRecheckContractId = TEXT("device-permission-recheck-v1");
|
||||
Profile.PermissionRequestActionId = TEXT("request-microphone-permission");
|
||||
Profile.OpenSettingsActionId = TEXT("open-microphone-privacy-settings");
|
||||
Profile.PermissionRecheckActionId = TEXT("recheck-microphone-permission");
|
||||
Profile.bSupportsPermissionRequest = true;
|
||||
Profile.bSupportsSettingsHandoff = true;
|
||||
Profile.bSupportsPermissionRecheck = 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("permission-workflow-summary-card"), TEXT("permission-workflow-summary-card"), TEXT("top-left"), true);
|
||||
AddPanel(TEXT("permission-settings-handoff-card"), TEXT("permission-settings-handoff-card"), TEXT("left-stack-below-summary"), true);
|
||||
AddPanel(TEXT("permission-recheck-card"), TEXT("permission-recheck-card"), TEXT("right-stack-aligned-summary"), true);
|
||||
|
||||
AddAction(TEXT("request-microphone-permission"), TEXT("P"), TEXT("permission-workflow-summary-card"));
|
||||
AddAction(TEXT("open-microphone-privacy-settings"), TEXT("S"), TEXT("permission-settings-handoff-card"));
|
||||
AddAction(TEXT("recheck-microphone-permission"), TEXT("R"), TEXT("permission-recheck-card"));
|
||||
AddAction(TEXT("close-speech-session"), TEXT("Escape"), TEXT("session-root"));
|
||||
return Profile;
|
||||
}
|
||||
|
||||
FHyperTwistSpeechUsageCostDashboardShellProfile MakeDefaultSpeechUsageCostDashboardShellProfile(
|
||||
const FString& ProfileId
|
||||
)
|
||||
|
|
@ -2467,6 +2522,11 @@ FHyperTwistSpeechSessionConfig UHyperTwistContractLibrary::MakeSampleSpeechSessi
|
|||
HyperTwistContractLibraryInternal::MakeDefaultCoachCommandMicrophoneShellProfile(
|
||||
Config.MicrophoneShellProfileId
|
||||
);
|
||||
Config.DevicePermissionWorkflowProfileId = TEXT("speech-device-permission-workflow-v1");
|
||||
Config.DevicePermissionWorkflowProfileDefinition =
|
||||
HyperTwistContractLibraryInternal::MakeDefaultSpeechDevicePermissionWorkflowProfile(
|
||||
Config.DevicePermissionWorkflowProfileId
|
||||
);
|
||||
Config.ProviderProfileId = TEXT("speech-provider/local-http-sidecar-profile-v1");
|
||||
Config.ProviderProfileDefinition =
|
||||
HyperTwistContractLibraryInternal::MakeLocalHttpSpeechProviderProfile(
|
||||
|
|
@ -2644,6 +2704,9 @@ FHyperTwistSpeechServiceHealth UHyperTwistContractLibrary::MakeMockSpeechService
|
|||
TEXT("devicePermissionShell"),
|
||||
TEXT("captureRouteReadinessShell"),
|
||||
TEXT("captureRouteRetry"),
|
||||
TEXT("devicePermissionWorkflow"),
|
||||
TEXT("devicePermissionSettingsHandoff"),
|
||||
TEXT("devicePermissionRecheck"),
|
||||
TEXT("streamingPreview"),
|
||||
TEXT("manualTranscriptCommit"),
|
||||
TEXT("batchedTranscribe"),
|
||||
|
|
|
|||
|
|
@ -324,6 +324,9 @@ FHyperTwistSpeechServiceHealth UHyperTwistHttpSpeechClient::GetSpeechServiceHeal
|
|||
TEXT("devicePermissionShell"),
|
||||
TEXT("captureRouteReadinessShell"),
|
||||
TEXT("captureRouteRetry"),
|
||||
TEXT("devicePermissionWorkflow"),
|
||||
TEXT("devicePermissionSettingsHandoff"),
|
||||
TEXT("devicePermissionRecheck"),
|
||||
TEXT("streamingPreview"),
|
||||
TEXT("manualTranscriptCommit"),
|
||||
TEXT("batchedTranscribe"),
|
||||
|
|
@ -413,6 +416,9 @@ FHyperTwistSpeechServiceHealth UHyperTwistHttpSpeechClient::GetSpeechServiceHeal
|
|||
TEXT("devicePermissionShell"),
|
||||
TEXT("captureRouteReadinessShell"),
|
||||
TEXT("captureRouteRetry"),
|
||||
TEXT("devicePermissionWorkflow"),
|
||||
TEXT("devicePermissionSettingsHandoff"),
|
||||
TEXT("devicePermissionRecheck"),
|
||||
TEXT("streamingPreview"),
|
||||
TEXT("manualTranscriptCommit"),
|
||||
TEXT("batchedTranscribe"),
|
||||
|
|
@ -478,6 +484,9 @@ FHyperTwistSpeechServiceHealth UHyperTwistHttpSpeechClient::GetSpeechServiceHeal
|
|||
Health.Capabilities.AddUnique(TEXT("devicePermissionShell"));
|
||||
Health.Capabilities.AddUnique(TEXT("captureRouteReadinessShell"));
|
||||
Health.Capabilities.AddUnique(TEXT("captureRouteRetry"));
|
||||
Health.Capabilities.AddUnique(TEXT("devicePermissionWorkflow"));
|
||||
Health.Capabilities.AddUnique(TEXT("devicePermissionSettingsHandoff"));
|
||||
Health.Capabilities.AddUnique(TEXT("devicePermissionRecheck"));
|
||||
Health.Capabilities.AddUnique(TEXT("streamingPreview"));
|
||||
Health.Capabilities.AddUnique(TEXT("manualTranscriptCommit"));
|
||||
Health.SupportedOrchestrationProfiles.AddUnique(TEXT("python-batch-transcribe-v1"));
|
||||
|
|
|
|||
|
|
@ -132,6 +132,9 @@ FHyperTwistSpeechServiceHealth UHyperTwistMockSpeechClient::GetSpeechServiceHeal
|
|||
Health.Capabilities.AddUnique(TEXT("devicePermissionShell"));
|
||||
Health.Capabilities.AddUnique(TEXT("captureRouteReadinessShell"));
|
||||
Health.Capabilities.AddUnique(TEXT("captureRouteRetry"));
|
||||
Health.Capabilities.AddUnique(TEXT("devicePermissionWorkflow"));
|
||||
Health.Capabilities.AddUnique(TEXT("devicePermissionSettingsHandoff"));
|
||||
Health.Capabilities.AddUnique(TEXT("devicePermissionRecheck"));
|
||||
Health.Capabilities.AddUnique(TEXT("streamingPreview"));
|
||||
Health.Capabilities.AddUnique(TEXT("manualTranscriptCommit"));
|
||||
Health.Capabilities.AddUnique(TEXT("batchedTranscribe"));
|
||||
|
|
|
|||
|
|
@ -2916,6 +2916,11 @@ namespace HyperTwistTrainingSubsystemInternal
|
|||
const bool bSessionOpen
|
||||
);
|
||||
|
||||
FHyperTwistSpeechDevicePermissionWorkflowState BuildSpeechDevicePermissionWorkflowState(
|
||||
const FHyperTwistSpeechSessionConfig& SessionConfig,
|
||||
const FHyperTwistTrainingCompanionSpeechSessionState* PriorState
|
||||
);
|
||||
|
||||
FHyperTwistSpeechUsageCostDashboardShellState BuildSpeechUsageCostDashboardShellState(
|
||||
const FHyperTwistSpeechSessionConfig& SessionConfig,
|
||||
const FHyperTwistTrainingCompanionSpeechSessionState* PriorState
|
||||
|
|
@ -3004,6 +3009,61 @@ namespace HyperTwistTrainingSubsystemInternal
|
|||
return Profile;
|
||||
}
|
||||
|
||||
FHyperTwistSpeechDevicePermissionWorkflowProfile
|
||||
BuildDefaultSpeechDevicePermissionWorkflowProfile(const FString& ProfileId)
|
||||
{
|
||||
FHyperTwistSpeechDevicePermissionWorkflowProfile Profile;
|
||||
Profile.DevicePermissionWorkflowProfileId = ProfileId;
|
||||
Profile.WorkflowKind = TEXT("operator-device-permission-workflow");
|
||||
Profile.PermissionContractId = TEXT("device-permission-workflow-v1");
|
||||
Profile.SettingsHandoffContractId = TEXT("device-permission-settings-handoff-v1");
|
||||
Profile.PermissionRecheckContractId = TEXT("device-permission-recheck-v1");
|
||||
Profile.PermissionRequestActionId = TEXT("request-microphone-permission");
|
||||
Profile.OpenSettingsActionId = TEXT("open-microphone-privacy-settings");
|
||||
Profile.PermissionRecheckActionId = TEXT("recheck-microphone-permission");
|
||||
Profile.bSupportsPermissionRequest = true;
|
||||
Profile.bSupportsSettingsHandoff = true;
|
||||
Profile.bSupportsPermissionRecheck = 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("permission-workflow-summary-card"), TEXT("permission-workflow-summary-card"), TEXT("top-left"), true);
|
||||
AddPanel(TEXT("permission-settings-handoff-card"), TEXT("permission-settings-handoff-card"), TEXT("left-stack-below-summary"), true);
|
||||
AddPanel(TEXT("permission-recheck-card"), TEXT("permission-recheck-card"), TEXT("right-stack-aligned-summary"), true);
|
||||
|
||||
AddAction(TEXT("request-microphone-permission"), TEXT("P"), TEXT("permission-workflow-summary-card"));
|
||||
AddAction(TEXT("open-microphone-privacy-settings"), TEXT("S"), TEXT("permission-settings-handoff-card"));
|
||||
AddAction(TEXT("recheck-microphone-permission"), TEXT("R"), TEXT("permission-recheck-card"));
|
||||
AddAction(TEXT("close-speech-session"), TEXT("Escape"), TEXT("session-root"));
|
||||
return Profile;
|
||||
}
|
||||
|
||||
FHyperTwistSpeechUsageCostDashboardShellProfile BuildDefaultSpeechUsageCostDashboardShellProfile(
|
||||
const FString& ProfileId
|
||||
)
|
||||
|
|
@ -3382,6 +3442,9 @@ namespace HyperTwistTrainingSubsystemInternal
|
|||
TEXT("devicePermissionShell"),
|
||||
TEXT("captureRouteReadinessShell"),
|
||||
TEXT("captureRouteRetry"),
|
||||
TEXT("devicePermissionWorkflow"),
|
||||
TEXT("devicePermissionSettingsHandoff"),
|
||||
TEXT("devicePermissionRecheck"),
|
||||
TEXT("streamingPreview"),
|
||||
TEXT("manualTranscriptCommit"),
|
||||
TEXT("batchedTranscribe"),
|
||||
|
|
@ -4486,15 +4549,38 @@ namespace HyperTwistTrainingSubsystemInternal
|
|||
Health.Capabilities.AddUnique(TEXT("providerSettlementExceptionShell"));
|
||||
Health.Capabilities.AddUnique(TEXT("providerExternalPortalHandoff"));
|
||||
Health.Capabilities.AddUnique(TEXT("providerSettlementExceptionReview"));
|
||||
Health.Capabilities.AddUnique(TEXT("devicePermissionWorkflow"));
|
||||
Health.Capabilities.AddUnique(TEXT("devicePermissionSettingsHandoff"));
|
||||
Health.Capabilities.AddUnique(TEXT("devicePermissionRecheck"));
|
||||
}
|
||||
|
||||
bool IsSpeechPermissionError(const FString& ErrorText)
|
||||
{
|
||||
const FString NormalizedError = ErrorText.ToLower();
|
||||
return NormalizedError.Contains(TEXT("permission"))
|
||||
|| NormalizedError.Contains(TEXT("not-allowed"))
|
||||
|| NormalizedError.Contains(TEXT("access-denied"));
|
||||
}
|
||||
bool IsSpeechPermissionError(const FString& ErrorText)
|
||||
{
|
||||
const FString NormalizedError = ErrorText.ToLower();
|
||||
return NormalizedError.Contains(TEXT("permission"))
|
||||
|| NormalizedError.Contains(TEXT("not-allowed"))
|
||||
|| NormalizedError.Contains(TEXT("access-denied"));
|
||||
}
|
||||
|
||||
bool IsSpeechPermissionSettingsError(const FString& ErrorText)
|
||||
{
|
||||
const FString NormalizedError = ErrorText.ToLower();
|
||||
return IsSpeechPermissionError(ErrorText)
|
||||
&& (NormalizedError.Contains(TEXT("denied"))
|
||||
|| NormalizedError.Contains(TEXT("blocked"))
|
||||
|| NormalizedError.Contains(TEXT("settings"))
|
||||
|| NormalizedError.Contains(TEXT("privacy")));
|
||||
}
|
||||
|
||||
bool IsSpeechPermissionRecheckError(const FString& ErrorText)
|
||||
{
|
||||
const FString NormalizedError = ErrorText.ToLower();
|
||||
return IsSpeechPermissionError(ErrorText)
|
||||
&& (NormalizedError.Contains(TEXT("recheck"))
|
||||
|| NormalizedError.Contains(TEXT("retry-permission"))
|
||||
|| NormalizedError.Contains(TEXT("verify-permission"))
|
||||
|| NormalizedError.Contains(TEXT("pending")));
|
||||
}
|
||||
|
||||
bool IsSpeechRouteError(const FString& ErrorText)
|
||||
{
|
||||
|
|
@ -4574,7 +4660,7 @@ namespace HyperTwistTrainingSubsystemInternal
|
|||
Issue.IssueKind = TEXT("permission-required");
|
||||
Issue.StatusLine = TEXT("Microphone permission is required before capture can start.");
|
||||
Issue.DetailLine =
|
||||
TEXT("Grant microphone access and reopen the shell. This packet only exposes shell posture; it does not own the OS permission workflow.");
|
||||
TEXT("Use the first-party permission workflow to request access, review system settings, and recheck before reopening capture. Native OS grant execution remains outside this packet.");
|
||||
Issue.RecommendedActionId = Profile.PermissionRequestActionId;
|
||||
AddSpeechMicrophoneShellIssue(ShellState, Issue);
|
||||
|
||||
|
|
@ -4707,6 +4793,197 @@ namespace HyperTwistTrainingSubsystemInternal
|
|||
);
|
||||
}
|
||||
|
||||
void AddSpeechDevicePermissionWorkflowIssue(
|
||||
FHyperTwistSpeechDevicePermissionWorkflowState& WorkflowState,
|
||||
const FHyperTwistSpeechDevicePermissionWorkflowIssue& Issue
|
||||
)
|
||||
{
|
||||
if (!Issue.IsStructurallyValid())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
WorkflowState.Issues.Add(Issue);
|
||||
if (!WorkflowState.ActiveIssue.IsStructurallyValid())
|
||||
{
|
||||
WorkflowState.ActiveIssue = Issue;
|
||||
}
|
||||
}
|
||||
|
||||
void ApplySpeechDevicePermissionWorkflowPosture(
|
||||
const FHyperTwistSpeechDevicePermissionWorkflowProfile& Profile,
|
||||
const FHyperTwistTrainingCompanionSpeechSessionState& SessionState,
|
||||
FHyperTwistSpeechDevicePermissionWorkflowState& WorkflowState
|
||||
)
|
||||
{
|
||||
const FString PermissionError = !SessionState.LastError.IsEmpty()
|
||||
? SessionState.LastError
|
||||
: SessionState.ServiceHealth.LastError;
|
||||
|
||||
WorkflowState.PermissionStateId = SessionState.MicrophoneShellState.IsStructurallyValid()
|
||||
? SessionState.MicrophoneShellState.PermissionStateId
|
||||
: TEXT("permission-granted");
|
||||
WorkflowState.WorkflowStateId = TEXT("permission-workflow-cleared");
|
||||
WorkflowState.LatestSourceKind = TEXT("permission-state-granted");
|
||||
WorkflowState.StatusLine = SessionState.bSessionOpen
|
||||
? TEXT("Microphone permission already granted.")
|
||||
: TEXT("Permission workflow ready when the speech shell opens.");
|
||||
WorkflowState.DetailLine = SessionState.bSessionOpen
|
||||
? TEXT("The first-party permission workflow is clear for the bounded speech shell.")
|
||||
: TEXT("Open the companion speech session to evaluate live permission posture.");
|
||||
WorkflowState.LastPermissionError = PermissionError;
|
||||
WorkflowState.WorkflowEntryCount = 0;
|
||||
WorkflowState.PermissionRequestEntryCount = 0;
|
||||
WorkflowState.SettingsHandoffEntryCount = 0;
|
||||
WorkflowState.PermissionRecheckEntryCount = 0;
|
||||
WorkflowState.bPermissionGranted = true;
|
||||
WorkflowState.bWorkflowSummaryReady = true;
|
||||
WorkflowState.bPermissionWorkflowReady = true;
|
||||
WorkflowState.bSettingsReviewRecommended = false;
|
||||
WorkflowState.bPermissionRequestVisible = false;
|
||||
WorkflowState.bSettingsHandoffVisible = false;
|
||||
WorkflowState.bPermissionRecheckVisible = false;
|
||||
WorkflowState.Entries.Reset();
|
||||
WorkflowState.Issues.Reset();
|
||||
WorkflowState.ActiveIssue = FHyperTwistSpeechDevicePermissionWorkflowIssue();
|
||||
WorkflowState.ActiveIssueCount = 0;
|
||||
|
||||
if (!PermissionError.IsEmpty() && IsSpeechPermissionError(PermissionError))
|
||||
{
|
||||
FHyperTwistSpeechDevicePermissionWorkflowEntry Entry;
|
||||
FHyperTwistSpeechDevicePermissionWorkflowIssue Issue;
|
||||
|
||||
if (IsSpeechPermissionSettingsError(PermissionError))
|
||||
{
|
||||
Entry.EntryId = TEXT("device-permission-settings-review");
|
||||
Entry.SourceKind = TEXT("permission-settings-review-required");
|
||||
Entry.WorkflowStateId = TEXT("permission-settings-review-required");
|
||||
Entry.StatusLine =
|
||||
TEXT("Microphone permission must be reviewed in system settings.");
|
||||
Entry.DetailLine = FString::Printf(
|
||||
TEXT("The speech lane reported '%s'. Review the system privacy or permissions screen, then recheck before reopening capture."),
|
||||
*PermissionError
|
||||
);
|
||||
Entry.RecommendedActionId = Profile.OpenSettingsActionId;
|
||||
Entry.bRequiresSettingsHandoff = true;
|
||||
Entry.bRequiresPermissionRecheck = true;
|
||||
Entry.bPermissionGranted = false;
|
||||
|
||||
Issue.IssueId = TEXT("device-permission-settings-review");
|
||||
Issue.IssueKind = TEXT("permission-settings-review-required");
|
||||
Issue.StatusLine = Entry.StatusLine;
|
||||
Issue.DetailLine = Entry.DetailLine;
|
||||
Issue.RecommendedActionId = Profile.OpenSettingsActionId;
|
||||
|
||||
WorkflowState.SettingsHandoffEntryCount = 1;
|
||||
WorkflowState.PermissionRecheckEntryCount = 1;
|
||||
WorkflowState.bSettingsReviewRecommended = true;
|
||||
WorkflowState.bSettingsHandoffVisible = Profile.bSupportsSettingsHandoff;
|
||||
WorkflowState.bPermissionRecheckVisible = Profile.bSupportsPermissionRecheck;
|
||||
}
|
||||
else if (IsSpeechPermissionRecheckError(PermissionError))
|
||||
{
|
||||
Entry.EntryId = TEXT("device-permission-recheck");
|
||||
Entry.SourceKind = TEXT("permission-recheck-required");
|
||||
Entry.WorkflowStateId = TEXT("permission-recheck-required");
|
||||
Entry.StatusLine =
|
||||
TEXT("Microphone permission must be rechecked before capture resumes.");
|
||||
Entry.DetailLine = FString::Printf(
|
||||
TEXT("The speech lane reported '%s'. Recheck permission posture before reopening the bounded shell."),
|
||||
*PermissionError
|
||||
);
|
||||
Entry.RecommendedActionId = Profile.PermissionRecheckActionId;
|
||||
Entry.bRequiresPermissionRecheck = true;
|
||||
Entry.bPermissionGranted = false;
|
||||
|
||||
Issue.IssueId = TEXT("device-permission-recheck");
|
||||
Issue.IssueKind = TEXT("permission-recheck-required");
|
||||
Issue.StatusLine = Entry.StatusLine;
|
||||
Issue.DetailLine = Entry.DetailLine;
|
||||
Issue.RecommendedActionId = Profile.PermissionRecheckActionId;
|
||||
|
||||
WorkflowState.PermissionRecheckEntryCount = 1;
|
||||
WorkflowState.bPermissionRecheckVisible = Profile.bSupportsPermissionRecheck;
|
||||
}
|
||||
else
|
||||
{
|
||||
Entry.EntryId = TEXT("device-permission-request");
|
||||
Entry.SourceKind = TEXT("permission-request-required");
|
||||
Entry.WorkflowStateId = TEXT("permission-request-required");
|
||||
Entry.StatusLine =
|
||||
TEXT("Microphone permission must be requested before capture starts.");
|
||||
Entry.DetailLine = PermissionError.IsEmpty()
|
||||
? TEXT("Use the first-party permission workflow to request microphone access before reopening capture.")
|
||||
: FString::Printf(
|
||||
TEXT("The speech lane reported '%s'. Request microphone access before reopening capture."),
|
||||
*PermissionError
|
||||
);
|
||||
Entry.RecommendedActionId = Profile.PermissionRequestActionId;
|
||||
Entry.bPermissionGranted = false;
|
||||
|
||||
Issue.IssueId = TEXT("device-permission-request");
|
||||
Issue.IssueKind = TEXT("permission-request-required");
|
||||
Issue.StatusLine = Entry.StatusLine;
|
||||
Issue.DetailLine = Entry.DetailLine;
|
||||
Issue.RecommendedActionId = Profile.PermissionRequestActionId;
|
||||
|
||||
WorkflowState.PermissionRequestEntryCount = 1;
|
||||
WorkflowState.bPermissionRequestVisible = Profile.bSupportsPermissionRequest;
|
||||
}
|
||||
|
||||
WorkflowState.Entries.Add(Entry);
|
||||
WorkflowState.WorkflowEntryCount = WorkflowState.Entries.Num();
|
||||
WorkflowState.WorkflowStateId = Entry.WorkflowStateId;
|
||||
WorkflowState.PermissionStateId = TEXT("permission-required");
|
||||
WorkflowState.LatestSourceKind = Entry.SourceKind;
|
||||
WorkflowState.StatusLine = Entry.StatusLine;
|
||||
WorkflowState.DetailLine = Entry.DetailLine;
|
||||
WorkflowState.bPermissionGranted = false;
|
||||
WorkflowState.bPermissionWorkflowReady = false;
|
||||
AddSpeechDevicePermissionWorkflowIssue(WorkflowState, Issue);
|
||||
}
|
||||
|
||||
WorkflowState.ActiveIssueCount = WorkflowState.Issues.Num();
|
||||
}
|
||||
|
||||
void SynchronizeSpeechDevicePermissionWorkflowState(
|
||||
FHyperTwistTrainingCompanionSpeechSessionState& SessionState
|
||||
)
|
||||
{
|
||||
FHyperTwistSpeechSessionConfig EffectiveConfig = SessionState.SessionConfig;
|
||||
if (EffectiveConfig.DevicePermissionWorkflowProfileId.IsEmpty())
|
||||
{
|
||||
EffectiveConfig.DevicePermissionWorkflowProfileId =
|
||||
TEXT("speech-device-permission-workflow-v1");
|
||||
}
|
||||
const FHyperTwistSpeechDevicePermissionWorkflowProfile Profile =
|
||||
EffectiveConfig.DevicePermissionWorkflowProfileDefinition.IsStructurallyValid()
|
||||
? EffectiveConfig.DevicePermissionWorkflowProfileDefinition
|
||||
: BuildDefaultSpeechDevicePermissionWorkflowProfile(
|
||||
!EffectiveConfig.DevicePermissionWorkflowProfileId.IsEmpty()
|
||||
? EffectiveConfig.DevicePermissionWorkflowProfileId
|
||||
: TEXT("speech-device-permission-workflow-v1")
|
||||
);
|
||||
|
||||
if (!SessionState.DevicePermissionWorkflowState.IsStructurallyValid())
|
||||
{
|
||||
SessionState.DevicePermissionWorkflowState =
|
||||
BuildSpeechDevicePermissionWorkflowState(EffectiveConfig, &SessionState);
|
||||
}
|
||||
|
||||
FHyperTwistSpeechDevicePermissionWorkflowState& WorkflowState =
|
||||
SessionState.DevicePermissionWorkflowState;
|
||||
WorkflowState.DevicePermissionWorkflowProfileId =
|
||||
Profile.DevicePermissionWorkflowProfileId;
|
||||
WorkflowState.AvailableActionIds.Reset();
|
||||
for (const FHyperTwistSpeechShellActionBinding& ActionBinding : Profile.ActionBindings)
|
||||
{
|
||||
WorkflowState.AvailableActionIds.AddUnique(ActionBinding.ActionId);
|
||||
}
|
||||
|
||||
ApplySpeechDevicePermissionWorkflowPosture(Profile, SessionState, WorkflowState);
|
||||
}
|
||||
|
||||
void AddSpeechUsageCostDashboardIssue(
|
||||
FHyperTwistSpeechUsageCostDashboardShellState& ShellState,
|
||||
const FHyperTwistSpeechUsageCostDashboardIssue& Issue
|
||||
|
|
@ -6513,6 +6790,46 @@ namespace HyperTwistTrainingSubsystemInternal
|
|||
return ShellState;
|
||||
}
|
||||
|
||||
FHyperTwistSpeechDevicePermissionWorkflowState BuildSpeechDevicePermissionWorkflowState(
|
||||
const FHyperTwistSpeechSessionConfig& SessionConfig,
|
||||
const FHyperTwistTrainingCompanionSpeechSessionState* PriorState
|
||||
)
|
||||
{
|
||||
const FHyperTwistSpeechDevicePermissionWorkflowProfile Profile =
|
||||
SessionConfig.DevicePermissionWorkflowProfileDefinition.IsStructurallyValid()
|
||||
? SessionConfig.DevicePermissionWorkflowProfileDefinition
|
||||
: BuildDefaultSpeechDevicePermissionWorkflowProfile(
|
||||
!SessionConfig.DevicePermissionWorkflowProfileId.IsEmpty()
|
||||
? SessionConfig.DevicePermissionWorkflowProfileId
|
||||
: TEXT("speech-device-permission-workflow-v1")
|
||||
);
|
||||
|
||||
FHyperTwistSpeechDevicePermissionWorkflowState WorkflowState;
|
||||
WorkflowState.DevicePermissionWorkflowProfileId =
|
||||
Profile.DevicePermissionWorkflowProfileId;
|
||||
for (const FHyperTwistSpeechShellActionBinding& ActionBinding : Profile.ActionBindings)
|
||||
{
|
||||
WorkflowState.AvailableActionIds.AddUnique(ActionBinding.ActionId);
|
||||
}
|
||||
|
||||
if (PriorState != nullptr
|
||||
&& PriorState->DevicePermissionWorkflowState.IsStructurallyValid()
|
||||
&& PriorState->DevicePermissionWorkflowState.DevicePermissionWorkflowProfileId
|
||||
== Profile.DevicePermissionWorkflowProfileId)
|
||||
{
|
||||
WorkflowState = PriorState->DevicePermissionWorkflowState;
|
||||
WorkflowState.DevicePermissionWorkflowProfileId =
|
||||
Profile.DevicePermissionWorkflowProfileId;
|
||||
WorkflowState.AvailableActionIds.Reset();
|
||||
for (const FHyperTwistSpeechShellActionBinding& ActionBinding : Profile.ActionBindings)
|
||||
{
|
||||
WorkflowState.AvailableActionIds.AddUnique(ActionBinding.ActionId);
|
||||
}
|
||||
}
|
||||
|
||||
return WorkflowState;
|
||||
}
|
||||
|
||||
FHyperTwistSpeechUsageCostDashboardShellState BuildSpeechUsageCostDashboardShellState(
|
||||
const FHyperTwistSpeechSessionConfig& SessionConfig,
|
||||
const FHyperTwistTrainingCompanionSpeechSessionState* PriorState
|
||||
|
|
@ -8379,6 +8696,13 @@ FHyperTwistTrainingCompanionSpeechSessionState UHyperTwistTrainingSubsystem::Get
|
|||
return ActiveCompanionSpeechSessionState;
|
||||
}
|
||||
|
||||
#if WITH_AUTOMATION_TESTS
|
||||
void UHyperTwistTrainingSubsystem::RefreshCompanionSpeechServiceHealthForAutomation()
|
||||
{
|
||||
RefreshCompanionSpeechServiceHealth();
|
||||
}
|
||||
#endif
|
||||
|
||||
TArray<FHyperTwistCoachSignal> UHyperTwistTrainingSubsystem::GetActiveCoachSignals() const
|
||||
{
|
||||
return ActiveCoachSignals;
|
||||
|
|
@ -12140,6 +12464,11 @@ bool UHyperTwistTrainingSubsystem::OpenActiveCompanionSpeechSession(FString& Out
|
|||
ActiveCompanionSpeechSessionState.FinalTranscriptCount = 0;
|
||||
ActiveCompanionSpeechSessionState.bHasTranscriptResult = false;
|
||||
ActiveCompanionSpeechSessionState.LastTranscriptResult = FHyperTwistSpeechTranscriptResult();
|
||||
ActiveCompanionSpeechSessionState.DevicePermissionWorkflowState =
|
||||
HyperTwistTrainingSubsystemInternal::BuildSpeechDevicePermissionWorkflowState(
|
||||
SessionConfig,
|
||||
nullptr
|
||||
);
|
||||
ActiveCompanionSpeechSessionState.UsageCostHistoryExportShellState =
|
||||
HyperTwistTrainingSubsystemInternal::BuildSpeechUsageCostHistoryExportShellState(
|
||||
SessionConfig,
|
||||
|
|
@ -12231,6 +12560,9 @@ FHyperTwistSpeechTranscriptResult UHyperTwistTrainingSubsystem::SubmitActiveComp
|
|||
HyperTwistTrainingSubsystemInternal::SynchronizeSpeechMicrophoneShellState(
|
||||
ActiveCompanionSpeechSessionState
|
||||
);
|
||||
HyperTwistTrainingSubsystemInternal::SynchronizeSpeechDevicePermissionWorkflowState(
|
||||
ActiveCompanionSpeechSessionState
|
||||
);
|
||||
HyperTwistTrainingSubsystemInternal::SynchronizeSpeechUsageCostDashboardState(
|
||||
ActiveCompanionSpeechSessionState
|
||||
);
|
||||
|
|
@ -12417,6 +12749,9 @@ FHyperTwistSpeechTranscriptResult UHyperTwistTrainingSubsystem::SubmitActiveComp
|
|||
HyperTwistTrainingSubsystemInternal::SynchronizeSpeechMicrophoneShellState(
|
||||
ActiveCompanionSpeechSessionState
|
||||
);
|
||||
HyperTwistTrainingSubsystemInternal::SynchronizeSpeechDevicePermissionWorkflowState(
|
||||
ActiveCompanionSpeechSessionState
|
||||
);
|
||||
HyperTwistTrainingSubsystemInternal::SynchronizeSpeechUsageCostDashboardState(
|
||||
ActiveCompanionSpeechSessionState
|
||||
);
|
||||
|
|
@ -13015,6 +13350,31 @@ FHyperTwistSpeechSessionConfig UHyperTwistTrainingSubsystem::BuildActiveCompanio
|
|||
SessionConfig.MicrophoneShellProfileDefinition.MicrophoneShellProfileId =
|
||||
SessionConfig.MicrophoneShellProfileId;
|
||||
}
|
||||
if (SessionConfig.DevicePermissionWorkflowProfileId.IsEmpty()
|
||||
&& SessionConfig.DevicePermissionWorkflowProfileDefinition.IsStructurallyValid())
|
||||
{
|
||||
SessionConfig.DevicePermissionWorkflowProfileId =
|
||||
SessionConfig.DevicePermissionWorkflowProfileDefinition
|
||||
.DevicePermissionWorkflowProfileId;
|
||||
}
|
||||
if (SessionConfig.DevicePermissionWorkflowProfileId.IsEmpty())
|
||||
{
|
||||
SessionConfig.DevicePermissionWorkflowProfileId =
|
||||
TEXT("speech-device-permission-workflow-v1");
|
||||
}
|
||||
if (!SessionConfig.DevicePermissionWorkflowProfileDefinition.IsStructurallyValid())
|
||||
{
|
||||
SessionConfig.DevicePermissionWorkflowProfileDefinition =
|
||||
HyperTwistTrainingSubsystemInternal::BuildDefaultSpeechDevicePermissionWorkflowProfile(
|
||||
SessionConfig.DevicePermissionWorkflowProfileId
|
||||
);
|
||||
}
|
||||
if (SessionConfig.DevicePermissionWorkflowProfileDefinition.DevicePermissionWorkflowProfileId
|
||||
.IsEmpty())
|
||||
{
|
||||
SessionConfig.DevicePermissionWorkflowProfileDefinition.DevicePermissionWorkflowProfileId =
|
||||
SessionConfig.DevicePermissionWorkflowProfileId;
|
||||
}
|
||||
SessionConfig.bEnableVad = true;
|
||||
if (!SessionConfig.VadPolicy.IsStructurallyValid())
|
||||
{
|
||||
|
|
@ -13175,6 +13535,9 @@ void UHyperTwistTrainingSubsystem::RefreshCompanionSpeechServiceHealth()
|
|||
HyperTwistTrainingSubsystemInternal::SynchronizeSpeechMicrophoneShellState(
|
||||
ActiveCompanionSpeechSessionState
|
||||
);
|
||||
HyperTwistTrainingSubsystemInternal::SynchronizeSpeechDevicePermissionWorkflowState(
|
||||
ActiveCompanionSpeechSessionState
|
||||
);
|
||||
HyperTwistTrainingSubsystemInternal::SynchronizeSpeechUsageCostDashboardState(
|
||||
ActiveCompanionSpeechSessionState
|
||||
);
|
||||
|
|
@ -13215,6 +13578,9 @@ void UHyperTwistTrainingSubsystem::RefreshCompanionSpeechServiceHealth()
|
|||
HyperTwistTrainingSubsystemInternal::SynchronizeSpeechMicrophoneShellState(
|
||||
ActiveCompanionSpeechSessionState
|
||||
);
|
||||
HyperTwistTrainingSubsystemInternal::SynchronizeSpeechDevicePermissionWorkflowState(
|
||||
ActiveCompanionSpeechSessionState
|
||||
);
|
||||
HyperTwistTrainingSubsystemInternal::SynchronizeSpeechUsageCostDashboardState(
|
||||
ActiveCompanionSpeechSessionState
|
||||
);
|
||||
|
|
|
|||
|
|
@ -1806,6 +1806,297 @@ struct FHyperTwistSpeechMicrophoneShellState
|
|||
}
|
||||
};
|
||||
|
||||
USTRUCT(BlueprintType)
|
||||
struct FHyperTwistSpeechDevicePermissionWorkflowProfile
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
FString DevicePermissionWorkflowProfileId;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
FString WorkflowKind = TEXT("operator-device-permission-workflow");
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
FString PermissionContractId = TEXT("device-permission-workflow-v1");
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
FString SettingsHandoffContractId = TEXT("device-permission-settings-handoff-v1");
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
FString PermissionRecheckContractId = TEXT("device-permission-recheck-v1");
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
FString PermissionRequestActionId = TEXT("request-microphone-permission");
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
FString OpenSettingsActionId = TEXT("open-microphone-privacy-settings");
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
FString PermissionRecheckActionId = TEXT("recheck-microphone-permission");
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
bool bSupportsPermissionRequest = true;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
bool bSupportsSettingsHandoff = true;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
bool bSupportsPermissionRecheck = true;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
TArray<FHyperTwistSpeechShellPanelLayout> Panels;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
TArray<FHyperTwistSpeechShellActionBinding> ActionBindings;
|
||||
|
||||
bool IsStructurallyValid() const
|
||||
{
|
||||
if (DevicePermissionWorkflowProfileId.IsEmpty()
|
||||
|| WorkflowKind.IsEmpty()
|
||||
|| PermissionContractId.IsEmpty()
|
||||
|| SettingsHandoffContractId.IsEmpty()
|
||||
|| PermissionRecheckContractId.IsEmpty()
|
||||
|| PermissionRequestActionId.IsEmpty()
|
||||
|| OpenSettingsActionId.IsEmpty()
|
||||
|| PermissionRecheckActionId.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 FHyperTwistSpeechDevicePermissionWorkflowEntry
|
||||
{
|
||||
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 StatusLine;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
FString DetailLine;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
FString RecommendedActionId;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
bool bRequiresSettingsHandoff = false;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
bool bRequiresPermissionRecheck = false;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
bool bPermissionGranted = true;
|
||||
|
||||
bool IsStructurallyValid() const
|
||||
{
|
||||
return !EntryId.IsEmpty()
|
||||
&& !SourceKind.IsEmpty()
|
||||
&& !WorkflowStateId.IsEmpty()
|
||||
&& !StatusLine.IsEmpty()
|
||||
&& !DetailLine.IsEmpty()
|
||||
&& !RecommendedActionId.IsEmpty();
|
||||
}
|
||||
};
|
||||
|
||||
USTRUCT(BlueprintType)
|
||||
struct FHyperTwistSpeechDevicePermissionWorkflowIssue
|
||||
{
|
||||
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 FHyperTwistSpeechDevicePermissionWorkflowState
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
FString DevicePermissionWorkflowProfileId;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
FString WorkflowStateId = TEXT("permission-workflow-cleared");
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
FString PermissionStateId = TEXT("permission-granted");
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
FString LatestSourceKind = TEXT("permission-state-granted");
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
FString StatusLine = TEXT("Microphone permission already granted.");
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
FString DetailLine =
|
||||
TEXT("The first-party permission workflow is clear for the bounded speech shell.");
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
FString LastPermissionError;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
int32 WorkflowEntryCount = 0;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
int32 PermissionRequestEntryCount = 0;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
int32 SettingsHandoffEntryCount = 0;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
int32 PermissionRecheckEntryCount = 0;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
bool bPermissionGranted = true;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
bool bWorkflowSummaryReady = true;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
bool bPermissionWorkflowReady = true;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
bool bSettingsReviewRecommended = false;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
bool bPermissionRequestVisible = false;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
bool bSettingsHandoffVisible = false;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
bool bPermissionRecheckVisible = false;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
int32 ActiveIssueCount = 0;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
FHyperTwistSpeechDevicePermissionWorkflowIssue ActiveIssue;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
TArray<FHyperTwistSpeechDevicePermissionWorkflowEntry> Entries;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
TArray<FHyperTwistSpeechDevicePermissionWorkflowIssue> Issues;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
TArray<FString> AvailableActionIds;
|
||||
|
||||
bool IsStructurallyValid() const
|
||||
{
|
||||
if (DevicePermissionWorkflowProfileId.IsEmpty()
|
||||
|| WorkflowStateId.IsEmpty()
|
||||
|| PermissionStateId.IsEmpty()
|
||||
|| LatestSourceKind.IsEmpty()
|
||||
|| StatusLine.IsEmpty()
|
||||
|| DetailLine.IsEmpty()
|
||||
|| WorkflowEntryCount < 0
|
||||
|| PermissionRequestEntryCount < 0
|
||||
|| SettingsHandoffEntryCount < 0
|
||||
|| PermissionRecheckEntryCount < 0
|
||||
|| ActiveIssueCount < 0
|
||||
|| AvailableActionIds.Num() <= 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (WorkflowEntryCount != Entries.Num()
|
||||
|| ActiveIssueCount != Issues.Num()
|
||||
|| PermissionRequestEntryCount > WorkflowEntryCount
|
||||
|| SettingsHandoffEntryCount > WorkflowEntryCount
|
||||
|| PermissionRecheckEntryCount > WorkflowEntryCount)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (ActiveIssueCount > 0 && !ActiveIssue.IsStructurallyValid())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
for (const FHyperTwistSpeechDevicePermissionWorkflowEntry& Entry : Entries)
|
||||
{
|
||||
if (!Entry.IsStructurallyValid())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
for (const FHyperTwistSpeechDevicePermissionWorkflowIssue& Issue : Issues)
|
||||
{
|
||||
if (!Issue.IsStructurallyValid())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
for (const FString& ActionId : AvailableActionIds)
|
||||
{
|
||||
if (ActionId.IsEmpty())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
USTRUCT(BlueprintType)
|
||||
struct FHyperTwistSpeechModelPayloadDescriptor
|
||||
{
|
||||
|
|
@ -4471,6 +4762,14 @@ struct FHyperTwistSpeechSessionConfig
|
|||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
FHyperTwistSpeechMicrophoneShellProfile MicrophoneShellProfileDefinition;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
FString DevicePermissionWorkflowProfileId =
|
||||
TEXT("speech-device-permission-workflow-v1");
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
FHyperTwistSpeechDevicePermissionWorkflowProfile
|
||||
DevicePermissionWorkflowProfileDefinition;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
FString ProviderProfileId = TEXT("speech-provider/local-http-sidecar-profile-v1");
|
||||
|
||||
|
|
@ -4577,6 +4876,8 @@ struct FHyperTwistSpeechSessionConfig
|
|||
|| TaskKind.IsEmpty()
|
||||
|| MicrophoneShellProfileId.IsEmpty()
|
||||
|| !MicrophoneShellProfileDefinition.IsStructurallyValid()
|
||||
|| DevicePermissionWorkflowProfileId.IsEmpty()
|
||||
|| !DevicePermissionWorkflowProfileDefinition.IsStructurallyValid()
|
||||
|| ProviderProfileId.IsEmpty()
|
||||
|| !ProviderProfileDefinition.IsStructurallyValid()
|
||||
|| ByokCustodyProfileId.IsEmpty()
|
||||
|
|
|
|||
|
|
@ -179,6 +179,10 @@ public:
|
|||
UFUNCTION(BlueprintPure, Category = "HyperTwist|Training|Speech")
|
||||
FHyperTwistTrainingCompanionSpeechSessionState GetActiveCompanionSpeechSessionState() const;
|
||||
|
||||
#if WITH_AUTOMATION_TESTS
|
||||
void RefreshCompanionSpeechServiceHealthForAutomation();
|
||||
#endif
|
||||
|
||||
UFUNCTION(BlueprintPure, Category = "HyperTwist|Training|Coach")
|
||||
TArray<FHyperTwistCoachSignal> GetActiveCoachSignals() const;
|
||||
|
||||
|
|
|
|||
|
|
@ -1940,6 +1940,9 @@ struct FHyperTwistTrainingCompanionSpeechSessionState
|
|||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
FHyperTwistSpeechMicrophoneShellState MicrophoneShellState;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
FHyperTwistSpeechDevicePermissionWorkflowState DevicePermissionWorkflowState;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
FHyperTwistSpeechUsageCostDashboardShellState UsageCostDashboardShellState;
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,267 @@
|
|||
// Copyright HyperTwist, Inc. All Rights Reserved.
|
||||
|
||||
#include "Misc/AutomationTest.h"
|
||||
|
||||
#include "Engine/GameInstance.h"
|
||||
#include "HyperTwistTraining/HyperTwistTrainingSubsystem.h"
|
||||
#include "UObject/UnrealType.h"
|
||||
|
||||
#if WITH_AUTOMATION_TESTS
|
||||
|
||||
namespace HyperTwistWhisperCppPhase6RAFTestInternal
|
||||
{
|
||||
FHyperTwistTrainingDeck MakeSpeechDeck()
|
||||
{
|
||||
FHyperTwistTrainingDeck Deck;
|
||||
Deck.DeckId = TEXT("phase6r-af/whispercpp-device-permission-workflow");
|
||||
Deck.Title = TEXT("Phase 6R-AF Whisper Device Permission Workflow");
|
||||
Deck.DeliveryModes = {
|
||||
EHyperTwistTrainingDeliveryMode::CoachReviewed
|
||||
};
|
||||
|
||||
FHyperTwistTrainingCase TrainingCase;
|
||||
TrainingCase.CaseId = TEXT("phase6r-af-case");
|
||||
TrainingCase.PuzzleId = TEXT("cube/3x3x3");
|
||||
TrainingCase.PromptKind = EHyperTwistTrainingPromptKind::Sequence;
|
||||
TrainingCase.PromptLabel = TEXT("Phase 6R-AF 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-af-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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
IMPLEMENT_SIMPLE_AUTOMATION_TEST(
|
||||
FHyperTwistWhisperCppPhase6RAFDevicePermissionWorkflowSessionConfigTest,
|
||||
"HyperTwist.Permissive.WhisperCpp.Phase6R.AF.DevicePermissionWorkflowSessionConfig",
|
||||
EAutomationTestFlags::EditorContext | EAutomationTestFlags::EngineFilter
|
||||
)
|
||||
|
||||
bool FHyperTwistWhisperCppPhase6RAFDevicePermissionWorkflowSessionConfigTest::RunTest(
|
||||
const FString& Parameters
|
||||
)
|
||||
{
|
||||
UHyperTwistTrainingSubsystem* TrainingSubsystem =
|
||||
HyperTwistWhisperCppPhase6RAFTestInternal::MakeSpeechSubsystem(
|
||||
TEXT("phase6r-af-config-session"),
|
||||
TEXT("mock")
|
||||
);
|
||||
TestNotNull(TEXT("The speech subsystem must be constructed for Phase 6R-AF."), TrainingSubsystem);
|
||||
if (TrainingSubsystem == nullptr)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
FString OpenError;
|
||||
TestTrue(TEXT("The device-permission 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 FHyperTwistSpeechDevicePermissionWorkflowProfile& Profile =
|
||||
SessionState.SessionConfig.DevicePermissionWorkflowProfileDefinition;
|
||||
|
||||
TestTrue(TEXT("The device-permission workflow route must expose a structurally valid profile."), Profile.IsStructurallyValid());
|
||||
TestEqual(TEXT("The workflow profile must preserve the first-party profile id."), Profile.DevicePermissionWorkflowProfileId, TEXT("speech-device-permission-workflow-v1"));
|
||||
TestEqual(TEXT("The workflow profile must preserve the workflow kind."), Profile.WorkflowKind, TEXT("operator-device-permission-workflow"));
|
||||
TestEqual(TEXT("The workflow profile must preserve the permission contract id."), Profile.PermissionContractId, TEXT("device-permission-workflow-v1"));
|
||||
TestEqual(TEXT("The workflow profile must preserve the settings handoff contract id."), Profile.SettingsHandoffContractId, TEXT("device-permission-settings-handoff-v1"));
|
||||
TestEqual(TEXT("The workflow profile must preserve the recheck contract id."), Profile.PermissionRecheckContractId, TEXT("device-permission-recheck-v1"));
|
||||
TestEqual(TEXT("The workflow profile must preserve the request action id."), Profile.PermissionRequestActionId, TEXT("request-microphone-permission"));
|
||||
TestEqual(TEXT("The workflow profile must preserve the settings handoff action id."), Profile.OpenSettingsActionId, TEXT("open-microphone-privacy-settings"));
|
||||
TestEqual(TEXT("The workflow profile must preserve the recheck action id."), Profile.PermissionRecheckActionId, TEXT("recheck-microphone-permission"));
|
||||
TestTrue(TEXT("The speech service health must expose device-permission workflow capability."), SessionState.ServiceHealth.Capabilities.Contains(TEXT("devicePermissionWorkflow")));
|
||||
TestTrue(TEXT("The speech service health must expose settings handoff capability."), SessionState.ServiceHealth.Capabilities.Contains(TEXT("devicePermissionSettingsHandoff")));
|
||||
TestTrue(TEXT("The speech service health must expose permission recheck capability."), SessionState.ServiceHealth.Capabilities.Contains(TEXT("devicePermissionRecheck")));
|
||||
return true;
|
||||
}
|
||||
|
||||
IMPLEMENT_SIMPLE_AUTOMATION_TEST(
|
||||
FHyperTwistWhisperCppPhase6RAFDevicePermissionWorkflowTransitionTest,
|
||||
"HyperTwist.Permissive.WhisperCpp.Phase6R.AF.DevicePermissionWorkflowTransitions",
|
||||
EAutomationTestFlags::EditorContext | EAutomationTestFlags::EngineFilter
|
||||
)
|
||||
|
||||
bool FHyperTwistWhisperCppPhase6RAFDevicePermissionWorkflowTransitionTest::RunTest(
|
||||
const FString& Parameters
|
||||
)
|
||||
{
|
||||
UHyperTwistTrainingSubsystem* TrainingSubsystem =
|
||||
HyperTwistWhisperCppPhase6RAFTestInternal::MakeSpeechSubsystem(
|
||||
TEXT("phase6r-af-transition-session"),
|
||||
TEXT("mock")
|
||||
);
|
||||
TestNotNull(TEXT("The speech subsystem must be constructed for Phase 6R-AF."), TrainingSubsystem);
|
||||
if (TrainingSubsystem == nullptr)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
FString OpenError;
|
||||
TestTrue(TEXT("The device-permission workflow route must open a speech session."), TrainingSubsystem->OpenActiveCompanionSpeechSession(OpenError));
|
||||
TestTrue(TEXT("Opening the speech session must not report an error."), OpenError.IsEmpty());
|
||||
|
||||
FHyperTwistTrainingCompanionSpeechSessionState SessionState =
|
||||
TrainingSubsystem->GetActiveCompanionSpeechSessionState();
|
||||
FHyperTwistSpeechDevicePermissionWorkflowState WorkflowState =
|
||||
SessionState.DevicePermissionWorkflowState;
|
||||
|
||||
TestTrue(TEXT("The granted workflow state must be structurally valid."), WorkflowState.IsStructurallyValid());
|
||||
TestEqual(TEXT("The granted workflow state must start cleared."), WorkflowState.WorkflowStateId, TEXT("permission-workflow-cleared"));
|
||||
TestEqual(TEXT("The granted workflow state must preserve permission-granted posture."), WorkflowState.PermissionStateId, TEXT("permission-granted"));
|
||||
TestTrue(TEXT("The granted workflow state must report permission granted."), WorkflowState.bPermissionGranted);
|
||||
TestTrue(TEXT("The granted workflow state must report workflow ready."), WorkflowState.bPermissionWorkflowReady);
|
||||
TestFalse(TEXT("The granted workflow state must keep permission request hidden."), WorkflowState.bPermissionRequestVisible);
|
||||
|
||||
HyperTwistWhisperCppPhase6RAFTestInternal::ForcePermissionError(
|
||||
TrainingSubsystem,
|
||||
TEXT("microphone-permission-required")
|
||||
);
|
||||
TrainingSubsystem->RefreshCompanionSpeechServiceHealthForAutomation();
|
||||
|
||||
SessionState = TrainingSubsystem->GetActiveCompanionSpeechSessionState();
|
||||
WorkflowState = SessionState.DevicePermissionWorkflowState;
|
||||
|
||||
TestTrue(TEXT("The request-required workflow state must remain structurally valid."), WorkflowState.IsStructurallyValid());
|
||||
TestEqual(TEXT("The request-required workflow state must preserve the request posture."), WorkflowState.WorkflowStateId, TEXT("permission-request-required"));
|
||||
TestEqual(TEXT("The request-required workflow state must preserve the permission-required posture."), WorkflowState.PermissionStateId, TEXT("permission-required"));
|
||||
TestFalse(TEXT("The request-required workflow state must report permission withheld."), WorkflowState.bPermissionGranted);
|
||||
TestFalse(TEXT("The request-required workflow state must block workflow ready."), WorkflowState.bPermissionWorkflowReady);
|
||||
TestTrue(TEXT("The request-required workflow state must surface the permission request action."), WorkflowState.bPermissionRequestVisible);
|
||||
TestFalse(TEXT("The request-required workflow state must keep settings handoff hidden."), WorkflowState.bSettingsHandoffVisible);
|
||||
TestFalse(TEXT("The request-required workflow state must keep permission recheck hidden."), WorkflowState.bPermissionRecheckVisible);
|
||||
TestEqual(TEXT("The request-required workflow state must retain one workflow entry."), WorkflowState.WorkflowEntryCount, 1);
|
||||
TestEqual(TEXT("The request-required workflow state must retain one request entry."), WorkflowState.PermissionRequestEntryCount, 1);
|
||||
TestEqual(TEXT("The request-required workflow state must classify the active issue correctly."), WorkflowState.ActiveIssue.IssueKind, TEXT("permission-request-required"));
|
||||
return true;
|
||||
}
|
||||
|
||||
IMPLEMENT_SIMPLE_AUTOMATION_TEST(
|
||||
FHyperTwistWhisperCppPhase6RAFDevicePermissionSettingsWorkflowTest,
|
||||
"HyperTwist.Permissive.WhisperCpp.Phase6R.AF.DevicePermissionSettingsWorkflow",
|
||||
EAutomationTestFlags::EditorContext | EAutomationTestFlags::EngineFilter
|
||||
)
|
||||
|
||||
bool FHyperTwistWhisperCppPhase6RAFDevicePermissionSettingsWorkflowTest::RunTest(
|
||||
const FString& Parameters
|
||||
)
|
||||
{
|
||||
UHyperTwistTrainingSubsystem* TrainingSubsystem =
|
||||
HyperTwistWhisperCppPhase6RAFTestInternal::MakeSpeechSubsystem(
|
||||
TEXT("phase6r-af-settings-session"),
|
||||
TEXT("mock")
|
||||
);
|
||||
TestNotNull(TEXT("The speech subsystem must be constructed for Phase 6R-AF."), TrainingSubsystem);
|
||||
if (TrainingSubsystem == nullptr)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
FString OpenError;
|
||||
TestTrue(TEXT("The settings-review workflow route must open a speech session."), TrainingSubsystem->OpenActiveCompanionSpeechSession(OpenError));
|
||||
TestTrue(TEXT("Opening the settings-review speech session must not report an error."), OpenError.IsEmpty());
|
||||
|
||||
HyperTwistWhisperCppPhase6RAFTestInternal::ForcePermissionError(
|
||||
TrainingSubsystem,
|
||||
TEXT("microphone-permission-denied-settings")
|
||||
);
|
||||
TrainingSubsystem->RefreshCompanionSpeechServiceHealthForAutomation();
|
||||
|
||||
const FHyperTwistTrainingCompanionSpeechSessionState SessionState =
|
||||
TrainingSubsystem->GetActiveCompanionSpeechSessionState();
|
||||
const FHyperTwistSpeechDevicePermissionWorkflowState& WorkflowState =
|
||||
SessionState.DevicePermissionWorkflowState;
|
||||
|
||||
TestTrue(TEXT("The settings-review workflow state must remain structurally valid."), WorkflowState.IsStructurallyValid());
|
||||
TestEqual(TEXT("The settings-review workflow must preserve the settings-review posture."), WorkflowState.WorkflowStateId, TEXT("permission-settings-review-required"));
|
||||
TestEqual(TEXT("The settings-review workflow must preserve permission-required posture."), WorkflowState.PermissionStateId, TEXT("permission-required"));
|
||||
TestEqual(TEXT("The settings-review workflow must preserve the settings-review source kind."), WorkflowState.LatestSourceKind, TEXT("permission-settings-review-required"));
|
||||
TestFalse(TEXT("The settings-review workflow must report permission withheld."), WorkflowState.bPermissionGranted);
|
||||
TestFalse(TEXT("The settings-review workflow must block workflow-ready posture."), WorkflowState.bPermissionWorkflowReady);
|
||||
TestTrue(TEXT("The settings-review workflow must recommend settings review."), WorkflowState.bSettingsReviewRecommended);
|
||||
TestTrue(TEXT("The settings-review workflow must surface settings handoff."), WorkflowState.bSettingsHandoffVisible);
|
||||
TestTrue(TEXT("The settings-review workflow must surface permission recheck."), WorkflowState.bPermissionRecheckVisible);
|
||||
TestEqual(TEXT("The settings-review workflow must retain one workflow entry."), WorkflowState.WorkflowEntryCount, 1);
|
||||
TestEqual(TEXT("The settings-review workflow must retain one settings handoff entry."), WorkflowState.SettingsHandoffEntryCount, 1);
|
||||
TestEqual(TEXT("The settings-review workflow must retain one permission recheck entry."), WorkflowState.PermissionRecheckEntryCount, 1);
|
||||
TestEqual(TEXT("The settings-review workflow must classify the active issue correctly."), WorkflowState.ActiveIssue.IssueKind, TEXT("permission-settings-review-required"));
|
||||
TestTrue(TEXT("The health surface must still advertise device-permission workflow capability."), SessionState.ServiceHealth.Capabilities.Contains(TEXT("devicePermissionWorkflow")));
|
||||
TestTrue(TEXT("The health surface must still advertise settings handoff capability."), SessionState.ServiceHealth.Capabilities.Contains(TEXT("devicePermissionSettingsHandoff")));
|
||||
TestTrue(TEXT("The health surface must still advertise permission recheck capability."), SessionState.ServiceHealth.Capabilities.Contains(TEXT("devicePermissionRecheck")));
|
||||
return true;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
@ -211,8 +211,13 @@ Status update on `2026-05-21`:
|
|||
external-portal handoff control pass is now consumed
|
||||
- the bounded first-party `Phase 6R-AE` provider settlement exception and external-portal handoff
|
||||
packet is now landed in current code
|
||||
- the current next bounded move is a source-backed `Phase 6R-AF` first-party real
|
||||
device-permission workflow preparation/control pass, not a new restrictive packet by default
|
||||
- the generic source-backed `Phase 6R-AF` first-party real device-permission workflow control
|
||||
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
|
||||
- use the repo-row README census, the portfolio standing refresh backfill, and the `2R-A`
|
||||
ownership contract for the current queue after that correction
|
||||
|
||||
|
|
@ -361,8 +366,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-AE` first-party provider settlement
|
||||
exception and external-portal handoff preparation/control pass
|
||||
- the next bounded move is a source-backed `Phase 6R-AG` first-party native capture-route
|
||||
ownership and workflow preparation/control pass
|
||||
|
||||
Companion docs:
|
||||
|
||||
|
|
@ -1618,6 +1623,8 @@ Approved working posture:
|
|||
profile and shell-state boundary above the existing transcript-session seam
|
||||
- the next bounded permissive implementation slice is now landed as device-permission and
|
||||
capture-route readiness shell posture above the existing microphone shell seam
|
||||
- the next bounded first-party implementation slice is now landed as real device-permission
|
||||
workflow posture above the existing permission/readiness shell seam
|
||||
- the next bounded permissive implementation slice is now landed as downloadable model and
|
||||
payload custody metadata above the existing transcript-session and shell seams
|
||||
- the next bounded first-party implementation slice is now landed as provider-profile and BYOK
|
||||
|
|
@ -1636,11 +1643,14 @@ Approved working posture:
|
|||
exception and external-portal handoff shell metadata above the existing transcript-session,
|
||||
shell, payload-custody, provider-custody, provider-routing, usage/cost, history/export,
|
||||
receipt-review, and settlement seams
|
||||
- the next bounded first-party implementation slice is now landed as real device-permission
|
||||
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 top-level provider/session contract remains first-party HyperTwist-owned and
|
||||
provider-neutral; this row does not own that lane
|
||||
- keep real device-permission workflow, 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 native capture-route ownership, 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
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,168 @@
|
|||
# HyperTwist Phase 6R-AF first-party real device-permission workflow implementation packet
|
||||
|
||||
Created on `2026-05-25`
|
||||
|
||||
## Status
|
||||
|
||||
- first-party HyperTwist packet
|
||||
- bounded `Phase 6R-AF` implementation slice
|
||||
|
||||
## Purpose
|
||||
|
||||
This packet lands the next bounded first-party slice above the landed speech
|
||||
session, microphone-shell, permission/readiness shell, 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 real device-permission workflow
|
||||
|
||||
It is not:
|
||||
|
||||
- a native audio-device route ownership 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_AF_FIRST_PARTY_REAL_DEVICE_PERMISSION_WORKFLOW_PREPARATION_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 real device-permission workflow owner
|
||||
- `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 permission-workflow seam through:
|
||||
|
||||
- retained recognition contract types for:
|
||||
- `FHyperTwistSpeechDevicePermissionWorkflowProfile`
|
||||
- `FHyperTwistSpeechDevicePermissionWorkflowEntry`
|
||||
- `FHyperTwistSpeechDevicePermissionWorkflowIssue`
|
||||
- `FHyperTwistSpeechDevicePermissionWorkflowState`
|
||||
- expanded `FHyperTwistSpeechSessionConfig`
|
||||
- expanded `FHyperTwistTrainingCompanionSpeechSessionState`
|
||||
- contract-library defaults for:
|
||||
- permission-workflow profile ids and definitions
|
||||
- permission panel ids and action bindings
|
||||
- service-health capability exposure
|
||||
- active companion session-config, service-health, and derived
|
||||
permission-workflow composition in:
|
||||
- `UHyperTwistTrainingSubsystem`
|
||||
- direct speech-client health capability exposure in:
|
||||
- `UHyperTwistHttpSpeechClient`
|
||||
- `UHyperTwistMockSpeechClient`
|
||||
- focused automation coverage in:
|
||||
- `HyperTwistWhisperCppPhase6RAFDevicePermissionWorkflowContractTest.cpp`
|
||||
|
||||
## Why this is still intentionally bounded
|
||||
|
||||
This packet lands the real device-permission workflow family only.
|
||||
|
||||
Still deferred:
|
||||
|
||||
- native audio-device route ownership beyond bounded readiness/workflow 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.AF` `3/3`
|
||||
|
||||
Regression automation validation:
|
||||
|
||||
- `HyperTwist.Permissive.WhisperCpp.Phase6R.AE` `3/3`
|
||||
- `HyperTwist.Permissive.WhisperCpp.Phase6R.V` `3/3`
|
||||
- `HyperTwist.Permissive.WhisperCpp.Phase6R.U` `3/3`
|
||||
- `HyperTwist.Permissive.WhisperCpp.Phase6R.E` `3/3`
|
||||
- `HyperTwist.Permissive.FasterWhisper.Phase6R.F` `3/3`
|
||||
- `HyperTwist.Permissive.Piper.Phase6R.G` `3/3`
|
||||
- `HyperTwist.Permissive.Coqui.Phase6R.H` `3/3`
|
||||
- `HyperTwist.CleanRoom.CubeDesk` `14/14`
|
||||
|
||||
Non-blocking warnings stayed limited to the existing Unreal headless/editor
|
||||
noise, the pre-existing plugin dependency warning on `UnrealMCP`, and the
|
||||
known `http:/vision/health` hostname-resolution warnings in provider-backed
|
||||
automation.
|
||||
|
||||
## Queue effect
|
||||
|
||||
This packet consumes the current `Phase 6R-AF` 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 for:
|
||||
- permission-workflow profile ids and definitions
|
||||
- request / settings handoff / recheck issue posture
|
||||
- bounded capability exposure for:
|
||||
- `devicePermissionWorkflow`
|
||||
- `devicePermissionSettingsHandoff`
|
||||
- `devicePermissionRecheck`
|
||||
- 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:
|
||||
- native audio-device route ownership beyond bounded readiness/workflow
|
||||
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:
|
||||
|
||||
- a source-backed `Phase 6R-AG` first-party native capture-route ownership and
|
||||
workflow preparation/control pass
|
||||
|
||||
Keep the future sequencing guards visible:
|
||||
|
||||
- first-party HyperTwist
|
||||
- keep the top-level provider/session contract, routing/policy lane,
|
||||
usage/cost governance lane, dashboard shell lane, history/export shell
|
||||
lane, receipt-review shell lane, settlement shell lane,
|
||||
exception/handoff shell lane, and permission-workflow lane first-party
|
||||
- `ggml-org/whisper.cpp`
|
||||
- keep code-license judgments separate from actual model or payload shipping
|
||||
review in any follow-on packet
|
||||
- `SYSTRAN/faster-whisper`
|
||||
- keep the row complementary to the landed provider/session, custody,
|
||||
routing, usage/cost, dashboard, history/export, receipt-review,
|
||||
settlement, exception/handoff, and permission-workflow seams rather than
|
||||
widening it into native route ownership, payment execution, or
|
||||
provider-portal ownership
|
||||
|
|
@ -0,0 +1,129 @@
|
|||
# HyperTwist Phase 6R-AF first-party real device-permission workflow preparation packet
|
||||
|
||||
Created on `2026-05-25`
|
||||
|
||||
## Status
|
||||
|
||||
- first-party HyperTwist packet
|
||||
- source-backed `Phase 6R-AF` preparation/control slice
|
||||
|
||||
## Purpose
|
||||
|
||||
This packet scopes the next bounded first-party slice above the landed speech
|
||||
session, microphone-shell, permission/readiness shell, 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 real device-permission workflow only
|
||||
|
||||
It is not:
|
||||
|
||||
- a native audio-device route ownership 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_V_WHISPER_CPP_DEVICE_PERMISSION_AND_CAPTURE_ROUTE_READINESS_SHELL_IMPLEMENTATION_PACKET_2026-05-24.md`
|
||||
- `docs/arch/HYPERTWIST_PHASE6R_AE_FIRST_PARTY_PROVIDER_SETTLEMENT_EXCEPTION_AND_EXTERNAL_PORTAL_HANDOFF_IMPLEMENTATION_PACKET_2026-05-25.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 real device-permission workflow owner
|
||||
- `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-AF` slice may land:
|
||||
|
||||
1. first-party permission-workflow profile ids and definitions
|
||||
2. derived permission-workflow entry, issue, and shell-state record shapes
|
||||
3. session-config and companion-session-state expansion for those shapes
|
||||
4. bounded capability exposure for:
|
||||
- `devicePermissionWorkflow`
|
||||
- `devicePermissionSettingsHandoff`
|
||||
- `devicePermissionRecheck`
|
||||
5. focused automation for:
|
||||
- permission-request-required posture
|
||||
- settings-review-required posture
|
||||
- permission-recheck posture
|
||||
|
||||
The packet must stay out of:
|
||||
|
||||
- native audio-device route ownership
|
||||
- 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:
|
||||
- permission-workflow profile
|
||||
- permission-workflow entry
|
||||
- permission-workflow issue
|
||||
- permission-workflow state
|
||||
- expanded speech session config
|
||||
- expanded companion speech session state
|
||||
- contract-library defaults for:
|
||||
- permission-workflow profile ids and definitions
|
||||
- action bindings and panel ids
|
||||
- service-health capability exposure
|
||||
- training-subsystem composition for:
|
||||
- bounded request / settings handoff / recheck posture
|
||||
- explicit separation between workflow posture and native route ownership
|
||||
- explicit separation between workflow posture and actual OS grant execution
|
||||
- direct speech-client health capability exposure for:
|
||||
- `devicePermissionWorkflow`
|
||||
- `devicePermissionSettingsHandoff`
|
||||
- `devicePermissionRecheck`
|
||||
|
||||
## Validation target
|
||||
|
||||
Validate with:
|
||||
|
||||
- Unreal build for `UnrealHyperTwistEditor Win64 Development`
|
||||
- focused automation:
|
||||
- `HyperTwist.Permissive.WhisperCpp.Phase6R.AF`
|
||||
- regressions:
|
||||
- `HyperTwist.Permissive.WhisperCpp.Phase6R.AE`
|
||||
- `HyperTwist.Permissive.WhisperCpp.Phase6R.V`
|
||||
- `HyperTwist.Permissive.WhisperCpp.Phase6R.U`
|
||||
- `HyperTwist.Permissive.WhisperCpp.Phase6R.E`
|
||||
- `HyperTwist.Permissive.FasterWhisper.Phase6R.F`
|
||||
- `HyperTwist.Permissive.Piper.Phase6R.G`
|
||||
- `HyperTwist.Permissive.Coqui.Phase6R.H`
|
||||
- `HyperTwist.CleanRoom.CubeDesk`
|
||||
|
||||
## Queue effect
|
||||
|
||||
If this packet lands cleanly, the next bounded move becomes:
|
||||
|
||||
- a source-backed `Phase 6R-AG` first-party native capture-route ownership and
|
||||
workflow preparation/control pass
|
||||
|
||||
Keep the narrow sequencing guards visible:
|
||||
|
||||
- keep native capture-route ownership separate from the landed
|
||||
permission-workflow posture
|
||||
- keep actual payment execution and provider-portal ownership separate from
|
||||
this packet
|
||||
- keep actual payload shipping separate from usage/cost governance
|
||||
- keep broad assistant-platform widening outside this packet
|
||||
|
|
@ -188,11 +188,15 @@ The next bounded move is now:
|
|||
external-portal handoff control pass is now consumed
|
||||
36. the bounded first-party `Phase 6R-AE` provider settlement exception and external-portal
|
||||
handoff packet is now landed in current code
|
||||
37. the next bounded move is a source-backed `Phase 6R-AF` first-party real
|
||||
device-permission workflow preparation/control pass
|
||||
38. keep the speech-lane guard visible:
|
||||
37. the generic source-backed `Phase 6R-AF` first-party real device-permission workflow
|
||||
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:
|
||||
- keep code-license judgments separate from model, voice, and payload-license review
|
||||
39. keep the provider-neutral speech-lane guard visible:
|
||||
41. 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
|
||||
|
|
|
|||
|
|
@ -224,8 +224,10 @@ Current landed ordering status:
|
|||
bounded first-party implementation anchor
|
||||
- provider settlement exception and external-portal handoff now also have a
|
||||
bounded first-party implementation anchor
|
||||
- real device-permission workflow is now the next bounded first-party gap
|
||||
above the landed permission/readiness shell posture
|
||||
- real device-permission workflow now also has a bounded first-party
|
||||
implementation anchor above the landed permission/readiness shell posture
|
||||
- native capture-route ownership and workflow is now the next bounded first-party
|
||||
gap above the landed readiness and permission-workflow seams
|
||||
- actual payment execution or provider-portal ownership remains later
|
||||
first-party work
|
||||
- provider-specific adapters remain later work
|
||||
|
|
|
|||
|
|
@ -131,6 +131,7 @@ 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. |
|
||||
| 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. |
|
||||
|
||||
|
|
@ -210,6 +211,7 @@ 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. |
|
||||
| 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. |
|
||||
|
|
|
|||
|
|
@ -187,10 +187,13 @@ Canonical discovery surfaces for roadmap interpretation:
|
|||
external-portal handoff control pass is now consumed
|
||||
- the bounded first-party `Phase 6R-AE` provider settlement exception and external-portal
|
||||
handoff packet is now landed in current code
|
||||
- the current next bounded move is a source-backed `Phase 6R-AF` first-party real
|
||||
device-permission workflow preparation/control pass, while keeping native capture-route
|
||||
ownership, actual payment execution, provider-portal ownership, and actual payload shipping
|
||||
separately deferred
|
||||
- the generic source-backed `Phase 6R-AF` first-party real device-permission workflow control
|
||||
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 repo-row implementation queue is now live from that `Phase 6R-A` entry point rather than
|
||||
waiting on another first-party packet
|
||||
|
||||
|
|
@ -277,14 +280,14 @@ 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-AF` first-party real
|
||||
device-permission workflow preparation/control pass.
|
||||
The next bounded move is a source-backed `Phase 6R-AG` first-party native
|
||||
capture-route ownership and workflow preparation/control pass.
|
||||
|
||||
Queue interpretation after that control pass:
|
||||
|
||||
- then continue with the retained repo-row queue
|
||||
- highest current repo-row queue pressure sits in:
|
||||
- the first-party real device-permission workflow remainder above the
|
||||
- the first-party native capture-route ownership remainder above the
|
||||
partially landed `whisper.cpp` and `faster-whisper` speech seams
|
||||
- `HactarCE/Hyperspeedcube` is now closed for the currently justified retained row:
|
||||
- landed:
|
||||
|
|
@ -455,15 +458,15 @@ Queue interpretation after that control pass:
|
|||
- viseme / gesture runtime integration
|
||||
- broad assistant-platform scope
|
||||
- the next queue shape is now:
|
||||
- first-party real device-permission workflow assessment above the landed
|
||||
speech session, shell, payload-custody, provider-custody, provider-routing,
|
||||
normalized usage/cost, operator-facing dashboard, history/export,
|
||||
receipt-review, settlement, and exception/handoff seams
|
||||
- first-party native capture-route ownership and workflow assessment 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-AF` control pass before any widening into native
|
||||
capture-route ownership, actual payment execution, provider-portal
|
||||
ownership, actual payload shipping, broad voice-output ownership, or broad
|
||||
assistant-platform scope
|
||||
- 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
|
||||
- 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:
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue