diff --git a/clients/swift/FractalClient/Sources/FractalClient/Models.swift b/clients/swift/FractalClient/Sources/FractalClient/Models.swift index cef10d7387b..4079c74d94d 100644 --- a/clients/swift/FractalClient/Sources/FractalClient/Models.swift +++ b/clients/swift/FractalClient/Sources/FractalClient/Models.swift @@ -601,8 +601,12 @@ public struct MissionResource: Codable, Sendable { public let id, kind, revision public struct MissionRunSummary: Codable, Sendable { public let id, requester, status, phase, mustAct, stateSince: String; public let startedAt, completedAt: UInt64?; public let generationID, lastProgress, after, deadline: String?; public let progress: JSONValue; public let currentSteps: [JSONValue]; public let blocker, eta: JSONValue?; public let outcome: MissionRunOutcome?; public let steps: [MissionStep]?; enum CodingKeys: String, CodingKey { case id, startedAt = "started_at", completedAt = "completed_at", generationID = "generation_id", requester, status, phase, progress, currentSteps = "current_steps", mustAct = "must_act", stateSince = "state_since", outcome, lastProgress = "last_progress", blocker, eta, after, deadline, steps } } public struct MissionRunOutcome: Codable, Sendable { public let status, reason, actor, at: String; public let previousStatus: String?; enum CodingKeys: String, CodingKey { case status, previousStatus = "previous_status", reason, actor, at } } /// Assignees and claimants are nullable ActorRef strings (agent, daemon, or person). -public struct MissionStep: Codable, Sendable { public let id, path, state, since: String; public let startedAt, completedAt: UInt64?; public let title, assignee, claimant, lastProgress, progressAt, blockedReason: String?; public let attempt: UInt; public let agentless, keepOpen: Bool?; public let blockers, goals, constraints: [String]?; public let loopRound, loopMaxRounds: UInt?; public let loopReason, nextWakeAt, wakeReason, claimExpiresAt, nudgedAt: String?; public let wake: MissionWake?; enum CodingKeys: String, CodingKey { case id, startedAt = "started_at", completedAt = "completed_at", path, title, state, attempt, assignee, claimant, agentless, keepOpen = "keep_open", since, lastProgress = "last_progress", progressAt = "progress_at", blockedReason = "blocked_reason", blockers, goals, constraints, loopRound = "loop_round", loopMaxRounds = "loop_max_rounds", loopReason = "loop_reason", nextWakeAt = "next_wake_at", wakeReason = "wake_reason", claimExpiresAt = "claim_expires_at", nudgedAt = "nudged_at", wake, attempts, waits, transitions, historyComplete = "history_complete", eta }; /// Mission detail only, on the runs it enriches; absent elsewhere. - public let attempts: [MissionStepAttempt]?; public let waits: [MissionStepWait]?; public let transitions: [MissionStepTransition]?; public let historyComplete: Bool?; public let eta: MissionStepEta? } +public struct MissionStep: Codable, Sendable { public let id, path, state, since: String; public let startedAt, completedAt: UInt64?; public let title, assignee, claimant, lastProgress, progressAt, blockedReason: String?; public let attempt: UInt; public let agentless, keepOpen: Bool?; public let blockers, goals, constraints: [String]?; public let loopRound, loopMaxRounds: UInt?; public let loopReason, nextWakeAt, wakeReason, claimExpiresAt, nudgedAt: String?; public let wake: MissionWake?; enum CodingKeys: String, CodingKey { case id, startedAt = "started_at", completedAt = "completed_at", path, title, state, attempt, assignee, claimant, agentless, keepOpen = "keep_open", since, lastProgress = "last_progress", progressAt = "progress_at", blockedReason = "blocked_reason", blockers, goals, constraints, loopRound = "loop_round", loopMaxRounds = "loop_max_rounds", loopReason = "loop_reason", program, nextWakeAt = "next_wake_at", wakeReason = "wake_reason", claimExpiresAt = "claim_expires_at", nudgedAt = "nudged_at", wake, attempts, waits, transitions, historyComplete = "history_complete", eta }; /// Mission detail only, on the runs it enriches; absent elsewhere. + public let program: MissionStepProgram?; public let attempts: [MissionStepAttempt]?; public let waits: [MissionStepWait]?; public let transitions: [MissionStepTransition]?; public let historyComplete: Bool?; public let eta: MissionStepEta? } +/// A program step's current attempt journal: each key's latest entry, its innermost open key and open fault. +public struct MissionStepProgram: Codable, Sendable { public let openKey, lastEntryAt: String?; public let entries: [MissionStepProgramEntry]; public let truncated: Bool; public let stalledAfterMS: UInt64; public let fault: MissionStepProgramFault?; enum CodingKeys: String, CodingKey { case openKey = "open_key", entries, truncated, lastEntryAt = "last_entry_at", stalledAfterMS = "stalled_after_ms", fault } } +public struct MissionStepProgramEntry: Codable, Sendable { public let key, status: String; public let startedAt, endedAt, error, child: String?; public let ms: UInt64?; enum CodingKeys: String, CodingKey { case key, status, startedAt = "started_at", endedAt = "ended_at", ms, error, child } } +public struct MissionStepProgramFault: Codable, Sendable { public let scope, reason: String } /// Outcome, wait kind and event words are open: a later word decodes as a plain string. public struct MissionStepAttempt: Codable, Sendable { public let n: UInt; public let claimant: String?; public let startedAt, endedAt: UInt64?; public let outcome: String; public let reason: String?; enum CodingKeys: String, CodingKey { case n, claimant, startedAt = "started_at", endedAt = "ended_at", outcome, reason } } public struct MissionStepWait: Codable, Sendable { public let kind: String; public let attempt: UInt; public let start: UInt64; public let end, until: UInt64?; public let reason: String? } diff --git a/clients/typescript/fractal-client/Models.generated.ts b/clients/typescript/fractal-client/Models.generated.ts index 36a108f28ca..1dbf2051980 100644 --- a/clients/typescript/fractal-client/Models.generated.ts +++ b/clients/typescript/fractal-client/Models.generated.ts @@ -2579,6 +2579,7 @@ export type MissionStep = { next_wake_at?: (Timestamp | null); nudged_at?: (Timestamp | null); path: string; + program?: MissionStepProgram; progress_at?: (Timestamp | null); since: Timestamp; started_at?: number | null; @@ -2626,6 +2627,28 @@ export type MissionStepPresentation = { ; }; +export type MissionStepProgram = { + entries: Array; + fault?: ({ + reason: string; + scope: string; +} | null); + last_entry_at?: (Timestamp | null); + open_key?: string | null; + stalled_after_ms: number; + truncated: boolean; +}; + +export type MissionStepProgramEntry = { + child?: string | null; + ended_at?: (Timestamp | null); + error?: string | null; + key: string; + ms?: number | null; + started_at?: (Timestamp | null); + status: ("started" | "completed" | "failed" | string); +}; + export type MissionStepTransition = { at: number; attempt: number; diff --git a/clients/typescript/fractal-client/ReadSchema.generated.ts b/clients/typescript/fractal-client/ReadSchema.generated.ts index c11b2a75b84..06b855864ca 100644 --- a/clients/typescript/fractal-client/ReadSchema.generated.ts +++ b/clients/typescript/fractal-client/ReadSchema.generated.ts @@ -2446,6 +2446,37 @@ export const MissionStepEta = /*#__PURE__*/ (() => Schema.Struct({ export type MissionStepEta = typeof MissionStepEta.Type export type MissionStepEtaEncoded = typeof MissionStepEta.Encoded +export const MissionStepProgramEntry = /*#__PURE__*/ (() => Schema.Struct({ + /** The child mission run or ask subject the key waits on. */ + "child": Schema.OptionFromOptionalNullOr(Schema.String, NULL_NONE).annotate({ description: "The child mission run or ask subject the key waits on." }), + "ended_at": Schema.OptionFromOptionalNullOr(Timestamp, NULL_NONE), + /** The typed failure with its `_tag` and fields, cut to 300 characters. */ + "error": Schema.OptionFromOptionalNullOr(Schema.String, NULL_NONE).annotate({ description: "The typed failure with its `_tag` and fields, cut to 300 characters." }), + "key": Schema.String, + /** The effect's duration. */ + "ms": Schema.OptionFromOptionalNullOr(Schema.Int.check(Schema.isGreaterThanOrEqualTo(0)), NULL_NONE).annotate({ description: "The effect's duration." }), + "started_at": Schema.OptionFromOptionalNullOr(Timestamp, NULL_NONE), + "status": openEnum(["started","completed","failed"]) +}).annotate({ identifier: "MissionStepProgramEntry" }))() +export type MissionStepProgramEntry = typeof MissionStepProgramEntry.Type +export type MissionStepProgramEntryEncoded = typeof MissionStepProgramEntry.Encoded + +export const MissionStepProgram = /*#__PURE__*/ (() => Schema.Struct({ + /** Each key's latest entry in this attempt, in the order the keys first appear; the newest 200 entries are read. */ + "entries": Schema.Array(MissionStepProgramEntry).annotate({ description: "Each key's latest entry in this attempt, in the order the keys first appear; the newest 200 entries are read." }), + /** The open `program/host` or `program/stalled` fault on the step. */ + "fault": Schema.OptionFromOptionalNullOr(Schema.Struct({ "reason": Schema.String, "scope": Schema.String }), NULL_NONE).annotate({ description: "The open `program/host` or `program/stalled` fault on the step." }), + "last_entry_at": Schema.OptionFromOptionalNullOr(Timestamp, NULL_NONE), + /** The innermost open key: the most recently started key without a result. */ + "open_key": Schema.OptionFromOptionalNullOr(Schema.String, NULL_NONE).annotate({ description: "The innermost open key: the most recently started key without a result." }), + /** How long the journal may go without a new entry before the step raises `program/stalled`. */ + "stalled_after_ms": Schema.Int.check(Schema.isGreaterThanOrEqualTo(1)).annotate({ description: "How long the journal may go without a new entry before the step raises `program/stalled`." }), + /** Older journal entries exist beyond the ones read. */ + "truncated": Schema.Boolean.annotate({ description: "Older journal entries exist beyond the ones read." }) +}).annotate({ identifier: "MissionStepProgram" }))() +export type MissionStepProgram = typeof MissionStepProgram.Type +export type MissionStepProgramEncoded = typeof MissionStepProgram.Encoded + export const MissionStepTransition = /*#__PURE__*/ (() => Schema.Struct({ /** Unix milliseconds. */ "at": Schema.Int.check(Schema.isGreaterThanOrEqualTo(0)).annotate({ description: "Unix milliseconds." }), @@ -2538,6 +2569,8 @@ export const MissionStep = /*#__PURE__*/ (() => Schema.Struct({ /** When st last nudged this held step's holder for idling with nothing set to wake it. */ "nudged_at": Schema.OptionFromOptionalNullOr(Timestamp, NULL_NONE).annotate({ description: "When st last nudged this held step's holder for idling with nothing set to wake it." }), "path": Schema.String, + /** Mission detail only, on the runs it enriches: a program step's current attempt journal. Absent for other steps and on older daemons. */ + "program": optionalKey(MissionStepProgram), /** Current attempt latest nonempty work.progress summary time; not lease renewal or seat activity. Lightweight mission list cards are unhydrated and return null, not proof of no progress. */ "progress_at": Schema.OptionFromOptionalNullOr(Timestamp, NULL_NONE).annotate({ description: "Current attempt latest nonempty work.progress summary time; not lease renewal or seat activity. Lightweight mission list cards are unhydrated and return null, not proof of no progress." }), "since": Timestamp, diff --git a/clients/typescript/fractal-client/Schema.generated.ts b/clients/typescript/fractal-client/Schema.generated.ts index c7216188d5b..88cbe8b570f 100644 --- a/clients/typescript/fractal-client/Schema.generated.ts +++ b/clients/typescript/fractal-client/Schema.generated.ts @@ -10,8 +10,8 @@ import type { SubjectRef, UnknownCase } from "./SchemaSupport.generated.ts" export { API_VERSION, containsUnknownCase, decodeUnknownSync, decodeUnknownEffect } from "./SchemaSupport.generated.ts" export type { SubjectRef, UnknownCase, DecodeMode } from "./SchemaSupport.generated.ts" -import { ActorRef, Agent, AgentCheckout, AgentDeclaration, AgentDelivery, AgentEndReason, AgentHandoff, AgentId, AgentLifecycle, AgentMailCustody, AgentMessageDay, AgentMessageEstimate, AgentPresentation, AgentPresentationNow, AgentPresentationWork, AgentQueue, AgentQueueMove, AgentQueueRun, AgentReachability, AgentRelatedRun, AgentRelatedRuns, AgentRelationship, AgentRepository, AgentState, AgentStopping, AgentSubagent, AgentSuspension, AgentTodo, AgentWorkspace, AgentsChanges, AgentsPublicationMetadata, AgentsPublicationSubscribe, AgentsSnapshotChunk, AgentsStatusWatermark, Arrangement, ArrangementBody, ArrangementBodyV1, ArrangementBodyV2, ArrangementFolder, ArrangementFolderId, ArrangementId, ArrangementKey, ArrangementName, ArrangementNameRegister, ArrangementPage, ArrangementPerson, ArrangementPlacement, ArrangementPlacementRegister, ArrangementPosition, ArrangementPositionRegister, ArrangementResolved, ArrangementSubject, ArrangementTombstoneRegister, AskDefaultAnswer, AssistantAgentDetailRequest, AssistantAgentItem, AssistantAgentPage, AssistantAttentionItem, AssistantAttentionPage, AssistantContentRange, AssistantConversationContent, AssistantConversationContentRequest, AssistantConversationSearchRequest, AssistantConversationTimelineRequest, AssistantMinimalReadMode, AssistantMissionItem, AssistantMissionPage, AssistantPageRequest, AssistantPrincipal, AssistantProjection, AssistantResponseBytes, AssistantSenderClass, AssistantSummary, AssistantSummaryRequest, Attachment, Attention, AttentionActor, AttentionBlocked, AttentionBudgetDeclared, AttentionBudgetReport, AttentionBudgetRequestRef, AttentionLaunchAgent, AttentionLaunchAssignee, AttentionLaunchPreview, AttentionLaunchStep, AttentionLaunchUnavailable, AttentionLaunchUnavailableReason, AttentionPresentation, AttentionPrompt, AttentionPromptCall, AttentionRevision, AttentionRevisionLine, AttentionTargetState, AttentionWaiting, BlobChunk, CanonicalNode, Capabilities, Capability, ClientConnection, ClientConnections, CollectionName, ConnectedClient, ConversationChanges, ConversationContentChunk, ConversationContentRef, ConversationHeader, ConversationHeaderField, ConversationSearch, ConversationSearchHit, Cursor, DecisionOption, DecisionResponse, DeclaredAdmission, DeclaredAttentionBudget, DeclaredBudgets, DeclaredLane, DeclaredPriority, DeclaredResource, Device, DeviceCommand, DeviceCommandState, Diagnostic, DirectiveNote, DirectiveNotes, DocumentContent, DocumentItem, DocumentReference, DocumentReferences, DocumentsPage, ErrorCode, EventPage, ExchangeTransport, Fence, Glass, GlassBody, GlassLayout, GlassTab, HarnessInventory, HarnessInventoryItem, HarnessPhase, HarnessQuestion, HarnessQuestionAnswerability, HarnessQuestionAnswerabilityReason, HarnessQuestionItem, HarnessQuestionResolution, HarnessResolvedAnswer, HarnessTask, HarnessTodoSnapshot, HarnessTodoTotals, History, HostId, HostRepositories, Id, JobSummary, Lane, LaneChange, LaneEntry, Launch, LaunchApproval, LaunchDecision, LaunchPreview, LaunchVariant, Machine, MachineCapacity, MachineOccupancy, MachineTransport, MailBacklog, Message, MessageBody, MessageProvenance, MessageStatusFrom, Mission, MissionActionOffer, MissionDeclaration, MissionDeclarationProvenance, MissionId, MissionPresentation, MissionQueuedBehind, MissionRunId, MissionRunOutcome, MissionRunSummary, MissionState, MissionStep, MissionStepAttempt, MissionStepEta, MissionStepPresentation, MissionStepTransition, MissionStepWait, MissionStuck, MissionWake, MustAct, Observer, ObserverSpec, Operation, Operational, OrderedMembership, OrderedMembershipPage, OrderedMembershipPosition, OrderedMembershipState, OwnedSet, Page, PageInfo, PairingAdvertisement, ParticipantRef, PersonAnswer, PersonAnswerRecord, PersonId, PersonOptionNote, PersonUpdate, PhoneInspectArgs, PhoneMessageReadArgs, PhoneNavigateArgs, PhoneRouteString, PlannerConfig, PresentationActor, PrivateNotesAction, PrivateNotesData, PrivateNotesFence, PrivateNotesSubject, PrivateNotesUri, ProjectionEvent, PublicationDefinition, PushEnvironment, PushRegistrationStatus, ReadCollectionCommand, ReadCollectionFrame, RelatedRunWait, ReplicatedNotice, RequestAnswerOption, RequestSubject, Resource, ResourceCollectionName, ResourceHeader, ResourceObservation, ResourcesPage, RespondentRef, Revision, RunGenerationId, RunOutcomeStatus, Runtime, RuntimeId, ScgReadAuthorization, ScgReadAuthorizationPayload, ScheduleCoverage, ScheduleDeclaration, ScheduleKnowledge, ScheduleOwner, ScheduleUnheardHost, SchedulesPage, SenderRef, Session, SloHistory, SloList, SloRow, Snapshot, SnapshotId, StatusHistory, StatusTransition, StepRunId, StreamSync, StructuredDiff, StructuredRequest, SubagentSummary, SubjectDefinition, SubscribeTraceContext, Subscription, SubscriptionSpec, Summary, SummaryMachines, SyncNotice, SyncPeer, TimelineAskAnswer, TimelineAskQuestion, TimelineBlock, TimelineContentBody, TimelineDelta, TimelineEntry, TimelineEntryId, TimelineErrorBody, TimelineMessageBody, TimelinePage, TimelineRecipientRef, TimelineRedactionBody, TimelineStatus, TimelineStatusBody, TimelineTodoItem, TimelineToolCallBody, TimelineToolResultBody, TimelineTruncationBody, TimelineUsageBody, TimelineView, TimelineViewAsk, TimelineViewAssistantError, TimelineViewBash, TimelineViewCompaction, TimelineViewCredentialPin, TimelineViewEdit, TimelineViewEval, TimelineViewGeneric, TimelineViewHub, TimelineViewIrc, TimelineViewJob, TimelineViewModelChange, TimelineViewOmpEvent, TimelineViewRead, TimelineViewResetBoundary, TimelineViewSearch, TimelineViewSessionExit, TimelineViewSkill, TimelineViewTask, TimelineViewThinkingLevel, TimelineViewTitle, TimelineViewTodo, TimelineViewToolStart, TimelineViewWrite, Timestamp, UnknownResource, Usage, UsageAccount, UsageContext, UsageCredits, UsageLimit, UsagePeriod, UsagePricing, UsagePricingRates, UsageRow, UsageSummary, Value, Visualization, VisualizationEdge, VisualizationGroup, VisualizationNode, Work, WorkLabel, WorkProgram, WorkState } from "./ReadSchema.generated.ts" -import type { ActorRefEncoded, AgentEncoded, AgentCheckoutEncoded, AgentDeclarationEncoded, AgentDeliveryEncoded, AgentEndReasonEncoded, AgentHandoffEncoded, AgentIdEncoded, AgentLifecycleEncoded, AgentMailCustodyEncoded, AgentMessageDayEncoded, AgentMessageEstimateEncoded, AgentPresentationEncoded, AgentPresentationNowEncoded, AgentPresentationWorkEncoded, AgentQueueEncoded, AgentQueueMoveEncoded, AgentQueueRunEncoded, AgentReachabilityEncoded, AgentRelatedRunEncoded, AgentRelatedRunsEncoded, AgentRelationshipEncoded, AgentRepositoryEncoded, AgentStateEncoded, AgentStoppingEncoded, AgentSubagentEncoded, AgentSuspensionEncoded, AgentTodoEncoded, AgentWorkspaceEncoded, AgentsChangesEncoded, AgentsPublicationMetadataEncoded, AgentsPublicationSubscribeEncoded, AgentsSnapshotChunkEncoded, AgentsStatusWatermarkEncoded, ArrangementEncoded, ArrangementBodyEncoded, ArrangementBodyV1Encoded, ArrangementBodyV2Encoded, ArrangementFolderEncoded, ArrangementFolderIdEncoded, ArrangementIdEncoded, ArrangementKeyEncoded, ArrangementNameEncoded, ArrangementNameRegisterEncoded, ArrangementPageEncoded, ArrangementPersonEncoded, ArrangementPlacementEncoded, ArrangementPlacementRegisterEncoded, ArrangementPositionEncoded, ArrangementPositionRegisterEncoded, ArrangementResolvedEncoded, ArrangementSubjectEncoded, ArrangementTombstoneRegisterEncoded, AskDefaultAnswerEncoded, AssistantAgentDetailRequestEncoded, AssistantAgentItemEncoded, AssistantAgentPageEncoded, AssistantAttentionItemEncoded, AssistantAttentionPageEncoded, AssistantContentRangeEncoded, AssistantConversationContentEncoded, AssistantConversationContentRequestEncoded, AssistantConversationSearchRequestEncoded, AssistantConversationTimelineRequestEncoded, AssistantMinimalReadModeEncoded, AssistantMissionItemEncoded, AssistantMissionPageEncoded, AssistantPageRequestEncoded, AssistantPrincipalEncoded, AssistantProjectionEncoded, AssistantResponseBytesEncoded, AssistantSenderClassEncoded, AssistantSummaryEncoded, AssistantSummaryRequestEncoded, AttachmentEncoded, AttentionEncoded, AttentionActorEncoded, AttentionBlockedEncoded, AttentionBudgetDeclaredEncoded, AttentionBudgetReportEncoded, AttentionBudgetRequestRefEncoded, AttentionLaunchAgentEncoded, AttentionLaunchAssigneeEncoded, AttentionLaunchPreviewEncoded, AttentionLaunchStepEncoded, AttentionLaunchUnavailableEncoded, AttentionLaunchUnavailableReasonEncoded, AttentionPresentationEncoded, AttentionPromptEncoded, AttentionPromptCallEncoded, AttentionRevisionEncoded, AttentionRevisionLineEncoded, AttentionTargetStateEncoded, AttentionWaitingEncoded, BlobChunkEncoded, CanonicalNodeEncoded, CapabilitiesEncoded, CapabilityEncoded, ClientConnectionEncoded, ClientConnectionsEncoded, CollectionNameEncoded, ConnectedClientEncoded, ConversationChangesEncoded, ConversationContentChunkEncoded, ConversationContentRefEncoded, ConversationHeaderEncoded, ConversationHeaderFieldEncoded, ConversationSearchEncoded, ConversationSearchHitEncoded, CursorEncoded, DecisionOptionEncoded, DecisionResponseEncoded, DeclaredAdmissionEncoded, DeclaredAttentionBudgetEncoded, DeclaredBudgetsEncoded, DeclaredLaneEncoded, DeclaredPriorityEncoded, DeclaredResourceEncoded, DeviceEncoded, DeviceCommandEncoded, DeviceCommandStateEncoded, DiagnosticEncoded, DirectiveNoteEncoded, DirectiveNotesEncoded, DocumentContentEncoded, DocumentItemEncoded, DocumentReferenceEncoded, DocumentReferencesEncoded, DocumentsPageEncoded, ErrorCodeEncoded, EventPageEncoded, ExchangeTransportEncoded, FenceEncoded, GlassEncoded, GlassBodyEncoded, GlassLayoutEncoded, GlassTabEncoded, HarnessInventoryEncoded, HarnessInventoryItemEncoded, HarnessPhaseEncoded, HarnessQuestionEncoded, HarnessQuestionAnswerabilityEncoded, HarnessQuestionAnswerabilityReasonEncoded, HarnessQuestionItemEncoded, HarnessQuestionResolutionEncoded, HarnessResolvedAnswerEncoded, HarnessTaskEncoded, HarnessTodoSnapshotEncoded, HarnessTodoTotalsEncoded, HistoryEncoded, HostIdEncoded, HostRepositoriesEncoded, IdEncoded, JobSummaryEncoded, LaneEncoded, LaneChangeEncoded, LaneEntryEncoded, LaunchEncoded, LaunchApprovalEncoded, LaunchDecisionEncoded, LaunchPreviewEncoded, LaunchVariantEncoded, MachineEncoded, MachineCapacityEncoded, MachineOccupancyEncoded, MachineTransportEncoded, MailBacklogEncoded, MessageEncoded, MessageBodyEncoded, MessageProvenanceEncoded, MessageStatusFromEncoded, MissionEncoded, MissionActionOfferEncoded, MissionDeclarationEncoded, MissionDeclarationProvenanceEncoded, MissionIdEncoded, MissionPresentationEncoded, MissionQueuedBehindEncoded, MissionRunIdEncoded, MissionRunOutcomeEncoded, MissionRunSummaryEncoded, MissionStateEncoded, MissionStepEncoded, MissionStepAttemptEncoded, MissionStepEtaEncoded, MissionStepPresentationEncoded, MissionStepTransitionEncoded, MissionStepWaitEncoded, MissionStuckEncoded, MissionWakeEncoded, MustActEncoded, ObserverEncoded, ObserverSpecEncoded, OperationEncoded, OperationalEncoded, OrderedMembershipEncoded, OrderedMembershipPageEncoded, OrderedMembershipPositionEncoded, OrderedMembershipStateEncoded, OwnedSetEncoded, PageEncoded, PageInfoEncoded, PairingAdvertisementEncoded, ParticipantRefEncoded, PersonAnswerEncoded, PersonAnswerRecordEncoded, PersonIdEncoded, PersonOptionNoteEncoded, PersonUpdateEncoded, PhoneInspectArgsEncoded, PhoneMessageReadArgsEncoded, PhoneNavigateArgsEncoded, PhoneRouteStringEncoded, PlannerConfigEncoded, PresentationActorEncoded, PrivateNotesActionEncoded, PrivateNotesDataEncoded, PrivateNotesFenceEncoded, PrivateNotesSubjectEncoded, PrivateNotesUriEncoded, ProjectionEventEncoded, PublicationDefinitionEncoded, PushEnvironmentEncoded, PushRegistrationStatusEncoded, ReadCollectionCommandEncoded, ReadCollectionFrameEncoded, RelatedRunWaitEncoded, ReplicatedNoticeEncoded, RequestAnswerOptionEncoded, RequestSubjectEncoded, ResourceEncoded, ResourceCollectionNameEncoded, ResourceHeaderEncoded, ResourceObservationEncoded, ResourcesPageEncoded, RespondentRefEncoded, RevisionEncoded, RunGenerationIdEncoded, RunOutcomeStatusEncoded, RuntimeEncoded, RuntimeIdEncoded, ScgReadAuthorizationEncoded, ScgReadAuthorizationPayloadEncoded, ScheduleCoverageEncoded, ScheduleDeclarationEncoded, ScheduleKnowledgeEncoded, ScheduleOwnerEncoded, ScheduleUnheardHostEncoded, SchedulesPageEncoded, SenderRefEncoded, SessionEncoded, SloHistoryEncoded, SloListEncoded, SloRowEncoded, SnapshotEncoded, SnapshotIdEncoded, StatusHistoryEncoded, StatusTransitionEncoded, StepRunIdEncoded, StreamSyncEncoded, StructuredDiffEncoded, StructuredRequestEncoded, SubagentSummaryEncoded, SubjectDefinitionEncoded, SubscribeTraceContextEncoded, SubscriptionEncoded, SubscriptionSpecEncoded, SummaryEncoded, SummaryMachinesEncoded, SyncNoticeEncoded, SyncPeerEncoded, TimelineAskAnswerEncoded, TimelineAskQuestionEncoded, TimelineBlockEncoded, TimelineContentBodyEncoded, TimelineDeltaEncoded, TimelineEntryEncoded, TimelineEntryIdEncoded, TimelineErrorBodyEncoded, TimelineMessageBodyEncoded, TimelinePageEncoded, TimelineRecipientRefEncoded, TimelineRedactionBodyEncoded, TimelineStatusEncoded, TimelineStatusBodyEncoded, TimelineTodoItemEncoded, TimelineToolCallBodyEncoded, TimelineToolResultBodyEncoded, TimelineTruncationBodyEncoded, TimelineUsageBodyEncoded, TimelineViewEncoded, TimelineViewAskEncoded, TimelineViewAssistantErrorEncoded, TimelineViewBashEncoded, TimelineViewCompactionEncoded, TimelineViewCredentialPinEncoded, TimelineViewEditEncoded, TimelineViewEvalEncoded, TimelineViewGenericEncoded, TimelineViewHubEncoded, TimelineViewIrcEncoded, TimelineViewJobEncoded, TimelineViewModelChangeEncoded, TimelineViewOmpEventEncoded, TimelineViewReadEncoded, TimelineViewResetBoundaryEncoded, TimelineViewSearchEncoded, TimelineViewSessionExitEncoded, TimelineViewSkillEncoded, TimelineViewTaskEncoded, TimelineViewThinkingLevelEncoded, TimelineViewTitleEncoded, TimelineViewTodoEncoded, TimelineViewToolStartEncoded, TimelineViewWriteEncoded, TimestampEncoded, UnknownResourceEncoded, UsageEncoded, UsageAccountEncoded, UsageContextEncoded, UsageCreditsEncoded, UsageLimitEncoded, UsagePeriodEncoded, UsagePricingEncoded, UsagePricingRatesEncoded, UsageRowEncoded, UsageSummaryEncoded, ValueEncoded, VisualizationEncoded, VisualizationEdgeEncoded, VisualizationGroupEncoded, VisualizationNodeEncoded, WorkEncoded, WorkLabelEncoded, WorkProgramEncoded, WorkStateEncoded } from "./ReadSchema.generated.ts" +import { ActorRef, Agent, AgentCheckout, AgentDeclaration, AgentDelivery, AgentEndReason, AgentHandoff, AgentId, AgentLifecycle, AgentMailCustody, AgentMessageDay, AgentMessageEstimate, AgentPresentation, AgentPresentationNow, AgentPresentationWork, AgentQueue, AgentQueueMove, AgentQueueRun, AgentReachability, AgentRelatedRun, AgentRelatedRuns, AgentRelationship, AgentRepository, AgentState, AgentStopping, AgentSubagent, AgentSuspension, AgentTodo, AgentWorkspace, AgentsChanges, AgentsPublicationMetadata, AgentsPublicationSubscribe, AgentsSnapshotChunk, AgentsStatusWatermark, Arrangement, ArrangementBody, ArrangementBodyV1, ArrangementBodyV2, ArrangementFolder, ArrangementFolderId, ArrangementId, ArrangementKey, ArrangementName, ArrangementNameRegister, ArrangementPage, ArrangementPerson, ArrangementPlacement, ArrangementPlacementRegister, ArrangementPosition, ArrangementPositionRegister, ArrangementResolved, ArrangementSubject, ArrangementTombstoneRegister, AskDefaultAnswer, AssistantAgentDetailRequest, AssistantAgentItem, AssistantAgentPage, AssistantAttentionItem, AssistantAttentionPage, AssistantContentRange, AssistantConversationContent, AssistantConversationContentRequest, AssistantConversationSearchRequest, AssistantConversationTimelineRequest, AssistantMinimalReadMode, AssistantMissionItem, AssistantMissionPage, AssistantPageRequest, AssistantPrincipal, AssistantProjection, AssistantResponseBytes, AssistantSenderClass, AssistantSummary, AssistantSummaryRequest, Attachment, Attention, AttentionActor, AttentionBlocked, AttentionBudgetDeclared, AttentionBudgetReport, AttentionBudgetRequestRef, AttentionLaunchAgent, AttentionLaunchAssignee, AttentionLaunchPreview, AttentionLaunchStep, AttentionLaunchUnavailable, AttentionLaunchUnavailableReason, AttentionPresentation, AttentionPrompt, AttentionPromptCall, AttentionRevision, AttentionRevisionLine, AttentionTargetState, AttentionWaiting, BlobChunk, CanonicalNode, Capabilities, Capability, ClientConnection, ClientConnections, CollectionName, ConnectedClient, ConversationChanges, ConversationContentChunk, ConversationContentRef, ConversationHeader, ConversationHeaderField, ConversationSearch, ConversationSearchHit, Cursor, DecisionOption, DecisionResponse, DeclaredAdmission, DeclaredAttentionBudget, DeclaredBudgets, DeclaredLane, DeclaredPriority, DeclaredResource, Device, DeviceCommand, DeviceCommandState, Diagnostic, DirectiveNote, DirectiveNotes, DocumentContent, DocumentItem, DocumentReference, DocumentReferences, DocumentsPage, ErrorCode, EventPage, ExchangeTransport, Fence, Glass, GlassBody, GlassLayout, GlassTab, HarnessInventory, HarnessInventoryItem, HarnessPhase, HarnessQuestion, HarnessQuestionAnswerability, HarnessQuestionAnswerabilityReason, HarnessQuestionItem, HarnessQuestionResolution, HarnessResolvedAnswer, HarnessTask, HarnessTodoSnapshot, HarnessTodoTotals, History, HostId, HostRepositories, Id, JobSummary, Lane, LaneChange, LaneEntry, Launch, LaunchApproval, LaunchDecision, LaunchPreview, LaunchVariant, Machine, MachineCapacity, MachineOccupancy, MachineTransport, MailBacklog, Message, MessageBody, MessageProvenance, MessageStatusFrom, Mission, MissionActionOffer, MissionDeclaration, MissionDeclarationProvenance, MissionId, MissionPresentation, MissionQueuedBehind, MissionRunId, MissionRunOutcome, MissionRunSummary, MissionState, MissionStep, MissionStepAttempt, MissionStepEta, MissionStepPresentation, MissionStepProgram, MissionStepProgramEntry, MissionStepTransition, MissionStepWait, MissionStuck, MissionWake, MustAct, Observer, ObserverSpec, Operation, Operational, OrderedMembership, OrderedMembershipPage, OrderedMembershipPosition, OrderedMembershipState, OwnedSet, Page, PageInfo, PairingAdvertisement, ParticipantRef, PersonAnswer, PersonAnswerRecord, PersonId, PersonOptionNote, PersonUpdate, PhoneInspectArgs, PhoneMessageReadArgs, PhoneNavigateArgs, PhoneRouteString, PlannerConfig, PresentationActor, PrivateNotesAction, PrivateNotesData, PrivateNotesFence, PrivateNotesSubject, PrivateNotesUri, ProjectionEvent, PublicationDefinition, PushEnvironment, PushRegistrationStatus, ReadCollectionCommand, ReadCollectionFrame, RelatedRunWait, ReplicatedNotice, RequestAnswerOption, RequestSubject, Resource, ResourceCollectionName, ResourceHeader, ResourceObservation, ResourcesPage, RespondentRef, Revision, RunGenerationId, RunOutcomeStatus, Runtime, RuntimeId, ScgReadAuthorization, ScgReadAuthorizationPayload, ScheduleCoverage, ScheduleDeclaration, ScheduleKnowledge, ScheduleOwner, ScheduleUnheardHost, SchedulesPage, SenderRef, Session, SloHistory, SloList, SloRow, Snapshot, SnapshotId, StatusHistory, StatusTransition, StepRunId, StreamSync, StructuredDiff, StructuredRequest, SubagentSummary, SubjectDefinition, SubscribeTraceContext, Subscription, SubscriptionSpec, Summary, SummaryMachines, SyncNotice, SyncPeer, TimelineAskAnswer, TimelineAskQuestion, TimelineBlock, TimelineContentBody, TimelineDelta, TimelineEntry, TimelineEntryId, TimelineErrorBody, TimelineMessageBody, TimelinePage, TimelineRecipientRef, TimelineRedactionBody, TimelineStatus, TimelineStatusBody, TimelineTodoItem, TimelineToolCallBody, TimelineToolResultBody, TimelineTruncationBody, TimelineUsageBody, TimelineView, TimelineViewAsk, TimelineViewAssistantError, TimelineViewBash, TimelineViewCompaction, TimelineViewCredentialPin, TimelineViewEdit, TimelineViewEval, TimelineViewGeneric, TimelineViewHub, TimelineViewIrc, TimelineViewJob, TimelineViewModelChange, TimelineViewOmpEvent, TimelineViewRead, TimelineViewResetBoundary, TimelineViewSearch, TimelineViewSessionExit, TimelineViewSkill, TimelineViewTask, TimelineViewThinkingLevel, TimelineViewTitle, TimelineViewTodo, TimelineViewToolStart, TimelineViewWrite, Timestamp, UnknownResource, Usage, UsageAccount, UsageContext, UsageCredits, UsageLimit, UsagePeriod, UsagePricing, UsagePricingRates, UsageRow, UsageSummary, Value, Visualization, VisualizationEdge, VisualizationGroup, VisualizationNode, Work, WorkLabel, WorkProgram, WorkState } from "./ReadSchema.generated.ts" +import type { ActorRefEncoded, AgentEncoded, AgentCheckoutEncoded, AgentDeclarationEncoded, AgentDeliveryEncoded, AgentEndReasonEncoded, AgentHandoffEncoded, AgentIdEncoded, AgentLifecycleEncoded, AgentMailCustodyEncoded, AgentMessageDayEncoded, AgentMessageEstimateEncoded, AgentPresentationEncoded, AgentPresentationNowEncoded, AgentPresentationWorkEncoded, AgentQueueEncoded, AgentQueueMoveEncoded, AgentQueueRunEncoded, AgentReachabilityEncoded, AgentRelatedRunEncoded, AgentRelatedRunsEncoded, AgentRelationshipEncoded, AgentRepositoryEncoded, AgentStateEncoded, AgentStoppingEncoded, AgentSubagentEncoded, AgentSuspensionEncoded, AgentTodoEncoded, AgentWorkspaceEncoded, AgentsChangesEncoded, AgentsPublicationMetadataEncoded, AgentsPublicationSubscribeEncoded, AgentsSnapshotChunkEncoded, AgentsStatusWatermarkEncoded, ArrangementEncoded, ArrangementBodyEncoded, ArrangementBodyV1Encoded, ArrangementBodyV2Encoded, ArrangementFolderEncoded, ArrangementFolderIdEncoded, ArrangementIdEncoded, ArrangementKeyEncoded, ArrangementNameEncoded, ArrangementNameRegisterEncoded, ArrangementPageEncoded, ArrangementPersonEncoded, ArrangementPlacementEncoded, ArrangementPlacementRegisterEncoded, ArrangementPositionEncoded, ArrangementPositionRegisterEncoded, ArrangementResolvedEncoded, ArrangementSubjectEncoded, ArrangementTombstoneRegisterEncoded, AskDefaultAnswerEncoded, AssistantAgentDetailRequestEncoded, AssistantAgentItemEncoded, AssistantAgentPageEncoded, AssistantAttentionItemEncoded, AssistantAttentionPageEncoded, AssistantContentRangeEncoded, AssistantConversationContentEncoded, AssistantConversationContentRequestEncoded, AssistantConversationSearchRequestEncoded, AssistantConversationTimelineRequestEncoded, AssistantMinimalReadModeEncoded, AssistantMissionItemEncoded, AssistantMissionPageEncoded, AssistantPageRequestEncoded, AssistantPrincipalEncoded, AssistantProjectionEncoded, AssistantResponseBytesEncoded, AssistantSenderClassEncoded, AssistantSummaryEncoded, AssistantSummaryRequestEncoded, AttachmentEncoded, AttentionEncoded, AttentionActorEncoded, AttentionBlockedEncoded, AttentionBudgetDeclaredEncoded, AttentionBudgetReportEncoded, AttentionBudgetRequestRefEncoded, AttentionLaunchAgentEncoded, AttentionLaunchAssigneeEncoded, AttentionLaunchPreviewEncoded, AttentionLaunchStepEncoded, AttentionLaunchUnavailableEncoded, AttentionLaunchUnavailableReasonEncoded, AttentionPresentationEncoded, AttentionPromptEncoded, AttentionPromptCallEncoded, AttentionRevisionEncoded, AttentionRevisionLineEncoded, AttentionTargetStateEncoded, AttentionWaitingEncoded, BlobChunkEncoded, CanonicalNodeEncoded, CapabilitiesEncoded, CapabilityEncoded, ClientConnectionEncoded, ClientConnectionsEncoded, CollectionNameEncoded, ConnectedClientEncoded, ConversationChangesEncoded, ConversationContentChunkEncoded, ConversationContentRefEncoded, ConversationHeaderEncoded, ConversationHeaderFieldEncoded, ConversationSearchEncoded, ConversationSearchHitEncoded, CursorEncoded, DecisionOptionEncoded, DecisionResponseEncoded, DeclaredAdmissionEncoded, DeclaredAttentionBudgetEncoded, DeclaredBudgetsEncoded, DeclaredLaneEncoded, DeclaredPriorityEncoded, DeclaredResourceEncoded, DeviceEncoded, DeviceCommandEncoded, DeviceCommandStateEncoded, DiagnosticEncoded, DirectiveNoteEncoded, DirectiveNotesEncoded, DocumentContentEncoded, DocumentItemEncoded, DocumentReferenceEncoded, DocumentReferencesEncoded, DocumentsPageEncoded, ErrorCodeEncoded, EventPageEncoded, ExchangeTransportEncoded, FenceEncoded, GlassEncoded, GlassBodyEncoded, GlassLayoutEncoded, GlassTabEncoded, HarnessInventoryEncoded, HarnessInventoryItemEncoded, HarnessPhaseEncoded, HarnessQuestionEncoded, HarnessQuestionAnswerabilityEncoded, HarnessQuestionAnswerabilityReasonEncoded, HarnessQuestionItemEncoded, HarnessQuestionResolutionEncoded, HarnessResolvedAnswerEncoded, HarnessTaskEncoded, HarnessTodoSnapshotEncoded, HarnessTodoTotalsEncoded, HistoryEncoded, HostIdEncoded, HostRepositoriesEncoded, IdEncoded, JobSummaryEncoded, LaneEncoded, LaneChangeEncoded, LaneEntryEncoded, LaunchEncoded, LaunchApprovalEncoded, LaunchDecisionEncoded, LaunchPreviewEncoded, LaunchVariantEncoded, MachineEncoded, MachineCapacityEncoded, MachineOccupancyEncoded, MachineTransportEncoded, MailBacklogEncoded, MessageEncoded, MessageBodyEncoded, MessageProvenanceEncoded, MessageStatusFromEncoded, MissionEncoded, MissionActionOfferEncoded, MissionDeclarationEncoded, MissionDeclarationProvenanceEncoded, MissionIdEncoded, MissionPresentationEncoded, MissionQueuedBehindEncoded, MissionRunIdEncoded, MissionRunOutcomeEncoded, MissionRunSummaryEncoded, MissionStateEncoded, MissionStepEncoded, MissionStepAttemptEncoded, MissionStepEtaEncoded, MissionStepPresentationEncoded, MissionStepProgramEncoded, MissionStepProgramEntryEncoded, MissionStepTransitionEncoded, MissionStepWaitEncoded, MissionStuckEncoded, MissionWakeEncoded, MustActEncoded, ObserverEncoded, ObserverSpecEncoded, OperationEncoded, OperationalEncoded, OrderedMembershipEncoded, OrderedMembershipPageEncoded, OrderedMembershipPositionEncoded, OrderedMembershipStateEncoded, OwnedSetEncoded, PageEncoded, PageInfoEncoded, PairingAdvertisementEncoded, ParticipantRefEncoded, PersonAnswerEncoded, PersonAnswerRecordEncoded, PersonIdEncoded, PersonOptionNoteEncoded, PersonUpdateEncoded, PhoneInspectArgsEncoded, PhoneMessageReadArgsEncoded, PhoneNavigateArgsEncoded, PhoneRouteStringEncoded, PlannerConfigEncoded, PresentationActorEncoded, PrivateNotesActionEncoded, PrivateNotesDataEncoded, PrivateNotesFenceEncoded, PrivateNotesSubjectEncoded, PrivateNotesUriEncoded, ProjectionEventEncoded, PublicationDefinitionEncoded, PushEnvironmentEncoded, PushRegistrationStatusEncoded, ReadCollectionCommandEncoded, ReadCollectionFrameEncoded, RelatedRunWaitEncoded, ReplicatedNoticeEncoded, RequestAnswerOptionEncoded, RequestSubjectEncoded, ResourceEncoded, ResourceCollectionNameEncoded, ResourceHeaderEncoded, ResourceObservationEncoded, ResourcesPageEncoded, RespondentRefEncoded, RevisionEncoded, RunGenerationIdEncoded, RunOutcomeStatusEncoded, RuntimeEncoded, RuntimeIdEncoded, ScgReadAuthorizationEncoded, ScgReadAuthorizationPayloadEncoded, ScheduleCoverageEncoded, ScheduleDeclarationEncoded, ScheduleKnowledgeEncoded, ScheduleOwnerEncoded, ScheduleUnheardHostEncoded, SchedulesPageEncoded, SenderRefEncoded, SessionEncoded, SloHistoryEncoded, SloListEncoded, SloRowEncoded, SnapshotEncoded, SnapshotIdEncoded, StatusHistoryEncoded, StatusTransitionEncoded, StepRunIdEncoded, StreamSyncEncoded, StructuredDiffEncoded, StructuredRequestEncoded, SubagentSummaryEncoded, SubjectDefinitionEncoded, SubscribeTraceContextEncoded, SubscriptionEncoded, SubscriptionSpecEncoded, SummaryEncoded, SummaryMachinesEncoded, SyncNoticeEncoded, SyncPeerEncoded, TimelineAskAnswerEncoded, TimelineAskQuestionEncoded, TimelineBlockEncoded, TimelineContentBodyEncoded, TimelineDeltaEncoded, TimelineEntryEncoded, TimelineEntryIdEncoded, TimelineErrorBodyEncoded, TimelineMessageBodyEncoded, TimelinePageEncoded, TimelineRecipientRefEncoded, TimelineRedactionBodyEncoded, TimelineStatusEncoded, TimelineStatusBodyEncoded, TimelineTodoItemEncoded, TimelineToolCallBodyEncoded, TimelineToolResultBodyEncoded, TimelineTruncationBodyEncoded, TimelineUsageBodyEncoded, TimelineViewEncoded, TimelineViewAskEncoded, TimelineViewAssistantErrorEncoded, TimelineViewBashEncoded, TimelineViewCompactionEncoded, TimelineViewCredentialPinEncoded, TimelineViewEditEncoded, TimelineViewEvalEncoded, TimelineViewGenericEncoded, TimelineViewHubEncoded, TimelineViewIrcEncoded, TimelineViewJobEncoded, TimelineViewModelChangeEncoded, TimelineViewOmpEventEncoded, TimelineViewReadEncoded, TimelineViewResetBoundaryEncoded, TimelineViewSearchEncoded, TimelineViewSessionExitEncoded, TimelineViewSkillEncoded, TimelineViewTaskEncoded, TimelineViewThinkingLevelEncoded, TimelineViewTitleEncoded, TimelineViewTodoEncoded, TimelineViewToolStartEncoded, TimelineViewWriteEncoded, TimestampEncoded, UnknownResourceEncoded, UsageEncoded, UsageAccountEncoded, UsageContextEncoded, UsageCreditsEncoded, UsageLimitEncoded, UsagePeriodEncoded, UsagePricingEncoded, UsagePricingRatesEncoded, UsageRowEncoded, UsageSummaryEncoded, ValueEncoded, VisualizationEncoded, VisualizationEdgeEncoded, VisualizationGroupEncoded, VisualizationNodeEncoded, WorkEncoded, WorkLabelEncoded, WorkProgramEncoded, WorkStateEncoded } from "./ReadSchema.generated.ts" export * from "./ReadSchema.generated.ts" export const ActionCommon = /*#__PURE__*/ (() => Schema.Struct({ diff --git a/crates/fractal-api-client-v0/src/api/client_v0.rs b/crates/fractal-api-client-v0/src/api/client_v0.rs index 581cf9057a0..334a373fcf8 100644 --- a/crates/fractal-api-client-v0/src/api/client_v0.rs +++ b/crates/fractal-api-client-v0/src/api/client_v0.rs @@ -3377,7 +3377,9 @@ fn mission_resources_filtered( }); let blocker = run.scheduler_fault.as_ref().map(|reason| json!({ "step": run.steps.first().map(|step| &step.subject), "reason": reason, "scope": crate::reconcile::FIRST_READINESS_FAULT_SCOPE, - })).or_else(|| + })).or_else(|| run.programs.iter().find_map(|program| program.fault.as_ref().map(|fault| json!({ + "step": program.step_run, "reason": fault.reason, "scope": fault.scope, + })))).or_else(|| run.steps.iter().find(|step| step.status == "blocked").map( |step| json!({"step": step.subject, "reason": step.blocked_reason}), )); @@ -3399,6 +3401,7 @@ fn mission_resources_filtered( let loop_run = run.loops.iter().find(|loop_run| { loop_run.step_run == step.subject }); + let program = run.programs.iter().find(|program| program.step_run == step.subject); let wake = step.wake.as_ref().filter(|_| { !matches!(run.status.as_str(), "completed" | "failed" | "cancelled") && !matches!(step.status.as_str(), "completed" | "failed" | "cancelled" | "skipped") @@ -3448,6 +3451,27 @@ fn mission_resources_filtered( value["transitions"] = json!(history.transitions); value["history_complete"] = json!(history.complete); } + // Detail reads that enrich the run (`Store::mission_run`) carry + // its program steps' journals. + if let Some(program) = program { + let journal = &program.journal; + value["program"] = json!({ + "open_key": journal.open.first(), + "entries": journal.entries.iter().map(|entry| json!({ + "key": entry.key, + "status": entry.status, + "started_at": entry.started_at_unix_ms.map(client_timestamp), + "ended_at": entry.ended_at_unix_ms.map(client_timestamp), + "ms": entry.ms, + "error": entry.error, + "child": entry.child, + })).collect::>(), + "truncated": journal.truncated, + "last_entry_at": journal.last_entry_at_unix_ms.map(client_timestamp), + "stalled_after_ms": program.stalled_after_ms, + "fault": program.fault.as_ref().map(|fault| json!({"scope": fault.scope, "reason": fault.reason})), + }); + } if let Some((low, high, samples)) = step_etas.get(&step.step) .filter(|_| !matches!(step.status.as_str(), "completed" | "failed" | "cancelled" | "skipped")) { diff --git a/crates/fractal-client-codegen/templates/Models.swift.in b/crates/fractal-client-codegen/templates/Models.swift.in index 1fd1bfe401b..6bc1e0389ab 100644 --- a/crates/fractal-client-codegen/templates/Models.swift.in +++ b/crates/fractal-client-codegen/templates/Models.swift.in @@ -601,8 +601,12 @@ public struct MissionResource: Codable, Sendable { public let id, kind, revision public struct MissionRunSummary: Codable, Sendable { public let id, requester, status, phase, mustAct, stateSince: String; public let startedAt, completedAt: UInt64?; public let generationID, lastProgress, after, deadline: String?; public let progress: JSONValue; public let currentSteps: [JSONValue]; public let blocker, eta: JSONValue?; public let outcome: MissionRunOutcome?; public let steps: [MissionStep]?; enum CodingKeys: String, CodingKey { case id, startedAt = "started_at", completedAt = "completed_at", generationID = "generation_id", requester, status, phase, progress, currentSteps = "current_steps", mustAct = "must_act", stateSince = "state_since", outcome, lastProgress = "last_progress", blocker, eta, after, deadline, steps } } public struct MissionRunOutcome: Codable, Sendable { public let status, reason, actor, at: String; public let previousStatus: String?; enum CodingKeys: String, CodingKey { case status, previousStatus = "previous_status", reason, actor, at } } /// Assignees and claimants are nullable ActorRef strings (agent, daemon, or person). -public struct MissionStep: Codable, Sendable { public let id, path, state, since: String; public let startedAt, completedAt: UInt64?; public let title, assignee, claimant, lastProgress, progressAt, blockedReason: String?; public let attempt: UInt; public let agentless, keepOpen: Bool?; public let blockers, goals, constraints: [String]?; public let loopRound, loopMaxRounds: UInt?; public let loopReason, nextWakeAt, wakeReason, claimExpiresAt, nudgedAt: String?; public let wake: MissionWake?; enum CodingKeys: String, CodingKey { case id, startedAt = "started_at", completedAt = "completed_at", path, title, state, attempt, assignee, claimant, agentless, keepOpen = "keep_open", since, lastProgress = "last_progress", progressAt = "progress_at", blockedReason = "blocked_reason", blockers, goals, constraints, loopRound = "loop_round", loopMaxRounds = "loop_max_rounds", loopReason = "loop_reason", nextWakeAt = "next_wake_at", wakeReason = "wake_reason", claimExpiresAt = "claim_expires_at", nudgedAt = "nudged_at", wake, attempts, waits, transitions, historyComplete = "history_complete", eta }; /// Mission detail only, on the runs it enriches; absent elsewhere. - public let attempts: [MissionStepAttempt]?; public let waits: [MissionStepWait]?; public let transitions: [MissionStepTransition]?; public let historyComplete: Bool?; public let eta: MissionStepEta? } +public struct MissionStep: Codable, Sendable { public let id, path, state, since: String; public let startedAt, completedAt: UInt64?; public let title, assignee, claimant, lastProgress, progressAt, blockedReason: String?; public let attempt: UInt; public let agentless, keepOpen: Bool?; public let blockers, goals, constraints: [String]?; public let loopRound, loopMaxRounds: UInt?; public let loopReason, nextWakeAt, wakeReason, claimExpiresAt, nudgedAt: String?; public let wake: MissionWake?; enum CodingKeys: String, CodingKey { case id, startedAt = "started_at", completedAt = "completed_at", path, title, state, attempt, assignee, claimant, agentless, keepOpen = "keep_open", since, lastProgress = "last_progress", progressAt = "progress_at", blockedReason = "blocked_reason", blockers, goals, constraints, loopRound = "loop_round", loopMaxRounds = "loop_max_rounds", loopReason = "loop_reason", program, nextWakeAt = "next_wake_at", wakeReason = "wake_reason", claimExpiresAt = "claim_expires_at", nudgedAt = "nudged_at", wake, attempts, waits, transitions, historyComplete = "history_complete", eta }; /// Mission detail only, on the runs it enriches; absent elsewhere. + public let program: MissionStepProgram?; public let attempts: [MissionStepAttempt]?; public let waits: [MissionStepWait]?; public let transitions: [MissionStepTransition]?; public let historyComplete: Bool?; public let eta: MissionStepEta? } +/// A program step's current attempt journal: each key's latest entry, its innermost open key and open fault. +public struct MissionStepProgram: Codable, Sendable { public let openKey, lastEntryAt: String?; public let entries: [MissionStepProgramEntry]; public let truncated: Bool; public let stalledAfterMS: UInt64; public let fault: MissionStepProgramFault?; enum CodingKeys: String, CodingKey { case openKey = "open_key", entries, truncated, lastEntryAt = "last_entry_at", stalledAfterMS = "stalled_after_ms", fault } } +public struct MissionStepProgramEntry: Codable, Sendable { public let key, status: String; public let startedAt, endedAt, error, child: String?; public let ms: UInt64?; enum CodingKeys: String, CodingKey { case key, status, startedAt = "started_at", endedAt = "ended_at", ms, error, child } } +public struct MissionStepProgramFault: Codable, Sendable { public let scope, reason: String } /// Outcome, wait kind and event words are open: a later word decodes as a plain string. public struct MissionStepAttempt: Codable, Sendable { public let n: UInt; public let claimant: String?; public let startedAt, endedAt: UInt64?; public let outcome: String; public let reason: String?; enum CodingKeys: String, CodingKey { case n, claimant, startedAt = "started_at", endedAt = "ended_at", outcome, reason } } public struct MissionStepWait: Codable, Sendable { public let kind: String; public let attempt: UInt; public let start: UInt64; public let end, until: UInt64?; public let reason: String? } diff --git a/crates/fractal-client-codegen/templates/generated.rs.in b/crates/fractal-client-codegen/templates/generated.rs.in index 34f6879cc03..5544f4ef4cc 100644 --- a/crates/fractal-client-codegen/templates/generated.rs.in +++ b/crates/fractal-client-codegen/templates/generated.rs.in @@ -1667,6 +1667,9 @@ pub struct MissionStep { pub loop_max_rounds: Option, #[serde(default)] pub loop_reason: Option, + /// Mission detail only: a program step's current attempt journal. Absent for other steps. + #[serde(default, skip_serializing_if = "Option::is_none")] + pub program: Option, /// Earliest work eligibility, not a promise of wake dispatch. #[serde(default)] pub next_wake_at: Option, @@ -1696,6 +1699,43 @@ pub struct MissionStep { pub eta: Option, } #[derive(Clone, Debug, Deserialize, Serialize, PartialEq)] +pub struct MissionStepProgram { + /// The most recently started key without a result. + #[serde(default)] + pub open_key: Option, + /// Each key's latest entry in this attempt, in the order the keys first appear. + pub entries: Vec, + /// Older journal entries exist beyond the ones read. + pub truncated: bool, + #[serde(default)] + pub last_entry_at: Option, + pub stalled_after_ms: u64, + /// The open `program/host` or `program/stalled` fault on the step. + #[serde(default)] + pub fault: Option, +} +#[derive(Clone, Debug, Deserialize, Serialize, PartialEq)] +pub struct MissionStepProgramEntry { + pub key: String, + /// `started`, `completed` or `failed`; a later word decodes as is. + pub status: String, + #[serde(default)] + pub started_at: Option, + #[serde(default)] + pub ended_at: Option, + #[serde(default)] + pub ms: Option, + #[serde(default)] + pub error: Option, + #[serde(default)] + pub child: Option, +} +#[derive(Clone, Debug, Deserialize, Serialize, PartialEq)] +pub struct MissionStepProgramFault { + pub scope: String, + pub reason: String, +} +#[derive(Clone, Debug, Deserialize, Serialize, PartialEq)] pub struct MissionStepAttempt { pub n: u64, /// An ActorRef: agent, daemon, or person. diff --git a/crates/fractal-client/src/generated.rs b/crates/fractal-client/src/generated.rs index 912535e5d51..7f14b474241 100644 --- a/crates/fractal-client/src/generated.rs +++ b/crates/fractal-client/src/generated.rs @@ -1668,6 +1668,9 @@ pub struct MissionStep { pub loop_max_rounds: Option, #[serde(default)] pub loop_reason: Option, + /// Mission detail only: a program step's current attempt journal. Absent for other steps. + #[serde(default, skip_serializing_if = "Option::is_none")] + pub program: Option, /// Earliest work eligibility, not a promise of wake dispatch. #[serde(default)] pub next_wake_at: Option, @@ -1697,6 +1700,43 @@ pub struct MissionStep { pub eta: Option, } #[derive(Clone, Debug, Deserialize, Serialize, PartialEq)] +pub struct MissionStepProgram { + /// The most recently started key without a result. + #[serde(default)] + pub open_key: Option, + /// Each key's latest entry in this attempt, in the order the keys first appear. + pub entries: Vec, + /// Older journal entries exist beyond the ones read. + pub truncated: bool, + #[serde(default)] + pub last_entry_at: Option, + pub stalled_after_ms: u64, + /// The open `program/host` or `program/stalled` fault on the step. + #[serde(default)] + pub fault: Option, +} +#[derive(Clone, Debug, Deserialize, Serialize, PartialEq)] +pub struct MissionStepProgramEntry { + pub key: String, + /// `started`, `completed` or `failed`; a later word decodes as is. + pub status: String, + #[serde(default)] + pub started_at: Option, + #[serde(default)] + pub ended_at: Option, + #[serde(default)] + pub ms: Option, + #[serde(default)] + pub error: Option, + #[serde(default)] + pub child: Option, +} +#[derive(Clone, Debug, Deserialize, Serialize, PartialEq)] +pub struct MissionStepProgramFault { + pub scope: String, + pub reason: String, +} +#[derive(Clone, Debug, Deserialize, Serialize, PartialEq)] pub struct MissionStepAttempt { pub n: u64, /// An ActorRef: agent, daemon, or person. diff --git a/crates/fractal/tests/client_v0_contract.rs b/crates/fractal/tests/client_v0_contract.rs index 2466dc215a0..269479b1469 100644 --- a/crates/fractal/tests/client_v0_contract.rs +++ b/crates/fractal/tests/client_v0_contract.rs @@ -4935,6 +4935,88 @@ mission "timing/run" state="ready" { assert_eq!(status, StatusCode::FORBIDDEN, "{denied}"); } +#[tokio::test] +async fn mission_detail_projects_a_program_steps_journal_and_its_fault() { + use sha2::Digest as _; + let root = tempfile::tempdir().unwrap(); + let state = test_state(root.path()); + let bundle = b"export default {}"; + let hash = hex::encode(sha2::Sha256::digest(bundle)); + state.store.put_blob(bundle).unwrap(); + let source = format!( + r#"version 2 +mission "program/land" state="ready" {{ + goal "Land one change by program." + step "land" {{ program "/bundles/land.mjs" sha256="{hash}" stalled-after="10m" }} +}} +"# + ); + let intent = fractal_daemon::graph::parse_intent(&source, state.store.origin()).unwrap(); + let planned = state.store.mission(&intent, fractal_daemon::model::IntentInput { + kdl: source.clone(), + source_name: None, + }).unwrap(); + state.store.apply(&intent, &planned.subject_tokens, "program-definition").unwrap(); + let run = state.store.create_mission_run(&fractal_daemon::model::MissionRunRequest { + mission: "program/land".into(), + revision: None, + workspace: root.path().display().to_string(), + requester: Some("person/avery".into()), + mode: Some("run".into()), + inputs: Default::default(), + idempotency_key: "program-run".into(), + }).unwrap(); + let land = run.steps.iter().find(|step| step.step == "land").unwrap().subject.clone(); + state.store.set_step_state(&land, "working", None).unwrap(); + let append = |kind: &str, fields: Value| { + state.store.append_claim(&fractal_daemon::model::ClaimInput { + subject: land.clone(), + kind: kind.into(), + actor: None, + fields: serde_json::from_value(fields).unwrap(), + evidence: Vec::new(), + expected_subject: None, + idempotency_key: None, + }).unwrap(); + }; + for (key, status, extra) in [ + ("merge", "started", serde_json::json!({})), + ("merge", "failed", serde_json::json!({"ms": 900, "error": {"_tag": "Conflict"}})), + ("review", "started", serde_json::json!({"child": "mission-run/program/review"})), + ] { + let mut fields = serde_json::json!({"key": key, "status": status, "attempt": 1, "program": hash}); + fields.as_object_mut().unwrap().extend(extra.as_object().unwrap().clone()); + append("program.step", fields); + } + append("reconcile.fault", serde_json::json!({ + "scope": "program/stalled", "status": "faulted", "reason": "no journal entry for 10m; open key `review`", + })); + + let app = fractal_daemon::api::router(state.clone()); + let path = "/v1/client/missions/mission%2Fprogram%2Fland"; + let (status, response) = client_json(app, path).await; + assert_eq!(status, StatusCode::OK, "{response}"); + assert_conforms(&contract_validator("Envelope"), path, &response); + let envelope: fractal_client::Envelope = + serde_json::from_value(response.clone()).unwrap(); + let fractal_client::Resource::Mission(mission) = envelope.value else { + panic!("mission detail must be a mission resource"); + }; + let detail = &mission.run_details[0]; + let step = detail.steps.as_ref().unwrap().iter().find(|step| step.path == "land").unwrap(); + let program = step.program.as_ref().expect("a program step carries its journal"); + assert_eq!(program.open_key.as_deref(), Some("review")); + assert_eq!((program.stalled_after_ms, program.truncated), (600_000, false)); + let entries = program.entries.iter().map(|entry| (entry.key.as_str(), entry.status.as_str())).collect::>(); + assert_eq!(entries, [("merge", "failed"), ("review", "started")]); + assert_eq!((program.entries[0].ms, program.entries[0].error.as_deref()), (Some(900), Some(r#"{"_tag":"Conflict"}"#))); + assert_eq!(program.entries[1].child.as_deref(), Some("mission-run/program/review")); + let fault = program.fault.as_ref().unwrap(); + assert_eq!(fault.scope, "program/stalled"); + let blocker = &response["value"]["run_details"][0]["blocker"]; + assert_eq!((blocker["step"].as_str(), blocker["scope"].as_str()), (Some(land.as_str()), Some("program/stalled"))); +} + #[tokio::test] async fn terminal_filters_use_projected_state_and_preserve_history_selection() { let root = tempfile::tempdir().unwrap(); diff --git a/docs/st3/client-v0/README.md b/docs/st3/client-v0/README.md index 94b5c615c74..035047a8f88 100644 --- a/docs/st3/client-v0/README.md +++ b/docs/st3/client-v0/README.md @@ -352,6 +352,13 @@ instead of assuming every assignee or claimant is an agent. `loop_round` and `loop_max_rounds` belong to the loop attached to that exact step in the current generation; they are null for a non-loop step. `loop_reason` is the observed loop-state reason. +`program`, present only on a program step of a run the detail enriches, is its current attempt's +journal: `entries` holds each key's latest `status` (`started`, `completed` or `failed`), +`started_at`, `ended_at`, `ms`, the typed `error`, and the `child` run or ask the key waits on, in +the order the keys first appear; the newest 200 journal entries are read and `truncated` says older +ones exist. `open_key` is the innermost open key, `last_entry_at` the newest entry, and `fault` the +open `program/host` or `program/stalled` fault (`scope`, `reason`), which the run's `blocker` also +names when no scheduler fault does. `next_wake_at` is the observed not-before time (earliest work eligibility), not a delivery promise or a computed retry estimate. `wake_reason` is the deferral's blocked reason, otherwise the observed wake failure. `wake` carries the assignee and its current harness state/incarnation, diff --git a/docs/st3/client-v0/schemas/client-v0.schema.json b/docs/st3/client-v0/schemas/client-v0.schema.json index 30e39499d50..abc5eccafef 100644 --- a/docs/st3/client-v0/schemas/client-v0.schema.json +++ b/docs/st3/client-v0/schemas/client-v0.schema.json @@ -2036,6 +2036,7 @@ "loop_round": { "type": ["integer", "null"], "minimum": 0, "description": "Current round of this step's loop, enriched on mission detail reads." }, "loop_max_rounds": { "type": ["integer", "null"], "minimum": 0 }, "loop_reason": { "type": ["string", "null"] }, + "program": { "$ref": "#/$defs/MissionStepProgram", "description": "Mission detail only, on the runs it enriches: a program step's current attempt journal. Absent for other steps and on older daemons." }, "next_wake_at": { "oneOf": [{ "$ref": "#/$defs/Timestamp" }, { "type": "null" }], "description": "Observed not-before time: earliest work eligibility, not a promise of wake dispatch." }, "wake_reason": { "type": ["string", "null"], "description": "The deferral's blocked reason, or the observed wake failure when no deferral reason exists." }, "wake": { "oneOf": [{ "$ref": "#/$defs/MissionWake" }, { "type": "null" }] }, @@ -2048,6 +2049,31 @@ "eta": { "$ref": "#/$defs/MissionStepEta" } } }, + "MissionStepProgram": { + "type": "object", + "required": ["entries", "truncated", "stalled_after_ms"], + "properties": { + "open_key": { "type": ["string", "null"], "description": "The innermost open key: the most recently started key without a result." }, + "entries": { "type": "array", "items": { "$ref": "#/$defs/MissionStepProgramEntry" }, "description": "Each key's latest entry in this attempt, in the order the keys first appear; the newest 200 entries are read." }, + "truncated": { "type": "boolean", "description": "Older journal entries exist beyond the ones read." }, + "last_entry_at": { "oneOf": [{ "$ref": "#/$defs/Timestamp" }, { "type": "null" }] }, + "stalled_after_ms": { "type": "integer", "minimum": 1, "description": "How long the journal may go without a new entry before the step raises `program/stalled`." }, + "fault": { "oneOf": [{ "type": "object", "required": ["scope", "reason"], "properties": { "scope": { "type": "string" }, "reason": { "type": "string" } } }, { "type": "null" }], "description": "The open `program/host` or `program/stalled` fault on the step." } + } + }, + "MissionStepProgramEntry": { + "type": "object", + "required": ["key", "status"], + "properties": { + "key": { "type": "string" }, + "status": { "anyOf": [{ "enum": ["started", "completed", "failed"] }, { "type": "string" }] }, + "started_at": { "oneOf": [{ "$ref": "#/$defs/Timestamp" }, { "type": "null" }] }, + "ended_at": { "oneOf": [{ "$ref": "#/$defs/Timestamp" }, { "type": "null" }] }, + "ms": { "type": ["integer", "null"], "minimum": 0, "description": "The effect's duration." }, + "error": { "type": ["string", "null"], "description": "The typed failure with its `_tag` and fields, cut to 300 characters." }, + "child": { "type": ["string", "null"], "description": "The child mission run or ask subject the key waits on." } + } + }, "MissionStepAttempt": { "type": "object", "required": ["n", "claimant", "started_at", "ended_at", "outcome"], diff --git a/docs/st3/program-engine.md b/docs/st3/program-engine.md index 775b08d8bec..dbbe95c91b1 100644 --- a/docs/st3/program-engine.md +++ b/docs/st3/program-engine.md @@ -345,7 +345,9 @@ Choice: a child run. It needs no standing owner and reuses ยง6. `program.step` claims under the program step: each key with its latest status, start and end, duration (`ms`), typed failure, and the child run or ask it waits on (`child`). The view folds a step's newest 200 entries and marks older ones `truncated`; the text lists the newest 12 keys. - The innermost open key is the most recently started key without a result. + The innermost open key is the most recently started key without a result. The run view (client + v0 mission detail, `run_details[].steps[].program`) carries the same journal with the step's + open program fault, which the run's `blocker` names when no scheduler fault does. - `missions waiting-on` gives a program step the kind `program` (never `watching`) and names its innermost open key: a running effect with how long it runs, or the child run or ask it waits on. The published work row carries that key (`program`, `gate_kind: program`), read from the step's diff --git a/release-notes/program-step-run-view.json b/release-notes/program-step-run-view.json new file mode 100644 index 00000000000..eadfe1db532 --- /dev/null +++ b/release-notes/program-step-run-view.json @@ -0,0 +1,11 @@ +{ + "version": 1, + "summary": "The run view (client v0 mission detail) shows a program step's journal: each key's latest status, timing, failure and child, the innermost open key, and the step's open program fault, which also becomes the run's blocker", + "replay": {"status": "none", "detail": "No claim kind or projection change; the detail reads the `programs` the enriched mission run view already carries."}, + "schema": {"status": "none", "detail": "No table, index or migration change. The client v0 schema gains the optional mission step field `program` (MissionStepProgram, MissionStepProgramEntry)."}, + "rules": {"status": "none", "detail": "No rule or authority change; the mission detail keeps its `read.projections` requirement."}, + "compatibility": {"status": "changed", "detail": "`run_details[].steps[].program` appears only on program steps of the runs a detail enriches (open runs, the latest finished run, a requested run). A run's `blocker` names an open `program/host` or `program/stalled` fault (with its `scope`) before a blocked step. Generated TypeScript, Swift and Rust clients model the new optional field; older clients ignore it."}, + "downtime": {"status": "none", "detail": "No restart or migration beyond a normal deploy."}, + "manual": {"status": "none", "detail": "No operator action is required."}, + "recovery": {"status": "none", "detail": "Read-only presentation; reverting removes the field."} +}