From a5605636aca8557b49b15525ef4eee73f541b96e Mon Sep 17 00:00:00 2001 From: 404ARE <936233544@qq.com> Date: Sat, 5 Sep 2026 10:44:42 +0800 Subject: [PATCH 1/2] refactor(workhub): clarify renderer decision seams --- apps/desktop/renderer-architecture.json | 10 +- .../main/__tests__/workhub-controller.test.ts | 8 +- .../workhub-session-resolver-port.test.ts | 2 +- .../__tests__/workhub-surface-flow.test.ts | 4 +- .../contracts/workhub-request-intent.ts | 30 -- .../renderer/application/contracts/workhub.ts | 282 ++++++++++++++++++ .../src/renderer/workhub-controller.ts | 265 +--------------- .../src/renderer/workhub-coordination-port.ts | 9 +- .../src/renderer/workhub-route-policy.ts | 25 +- .../src/renderer/workhub-session-port.ts | 4 +- apps/desktop/src/renderer/workhub-surface.tsx | 4 +- 11 files changed, 328 insertions(+), 315 deletions(-) delete mode 100644 apps/desktop/src/renderer/application/contracts/workhub-request-intent.ts create mode 100644 apps/desktop/src/renderer/application/contracts/workhub.ts diff --git a/apps/desktop/renderer-architecture.json b/apps/desktop/renderer-architecture.json index 4570aa0895..6fc52f373e 100644 --- a/apps/desktop/renderer-architecture.json +++ b/apps/desktop/renderer-architecture.json @@ -4533,6 +4533,7 @@ "unresolvedDependencies": 0, "actionFactories": [], "dependencyPaths": { + "./application/contracts/workhub.js": 1, "./workhub-route-policy.js": 1 } }, @@ -4566,8 +4567,8 @@ "unresolvedDependencies": 0, "actionFactories": [], "dependencyPaths": { + "./application/contracts/workhub.js": 1, "./desktop-transcript-range-store.js": 1, - "./workhub-controller.js": 1, "@maka/core/session": 1 } }, @@ -4579,7 +4580,8 @@ "unresolvedDependencies": 0, "actionFactories": [], "dependencyPaths": { - "./application/contracts/workhub-request-intent.js": 1 + "@maka/core/workhub-creation-intent": 1, + "@maka/core/workhub-session-resolver": 1 } }, "src/renderer/workhub-send-lease.ts": { @@ -4607,8 +4609,8 @@ "actionFactories": [], "dependencyPaths": { "../shared/runtime-host-identity.js": 1, + "./application/contracts/workhub.js": 1, "./desktop-transcript-range-store.js": 1, - "./workhub-controller.js": 1, "@maka/core/session": 1 } }, @@ -4624,7 +4626,7 @@ "unresolvedDependencies": 0, "actionFactories": [], "dependencyPaths": { - "./workhub-coordination-port.js": 1, + "./application/contracts/workhub.js": 1, "./workhub-send-lease.js": 1, "@astryxdesign/core": 1, "@astryxdesign/core/Button": 1, diff --git a/apps/desktop/src/main/__tests__/workhub-controller.test.ts b/apps/desktop/src/main/__tests__/workhub-controller.test.ts index 13b46ce35c..4972026402 100644 --- a/apps/desktop/src/main/__tests__/workhub-controller.test.ts +++ b/apps/desktop/src/main/__tests__/workhub-controller.test.ts @@ -21,18 +21,18 @@ import assert from 'node:assert/strict'; import { existsSync, readFileSync } from 'node:fs'; import test from 'node:test'; import type { WorkHubCoordinationActInput } from '@maka/runtime-host/protocol'; +import { createWorkHubController as createGatedWorkHubController } from '../../renderer/workhub-controller.js'; import { - createWorkHubController as createGatedWorkHubController, WORKHUB_ROUTING_STRATEGY_ID, + WorkHubCoordinationFailure, + type WorkHubCoordinationTurn, type WorkHubSessionFacts, type WorkHubSessionPort, - type WorkHubCoordinationTurn, -} from '../../renderer/workhub-controller.js'; +} from '../../renderer/application/contracts/workhub.js'; import { createWorkHubRoutePolicy, workHubNewSessionName, } from '../../renderer/workhub-route-policy.js'; -import { WorkHubCoordinationFailure } from '../../renderer/workhub-coordination-port.js'; const appShellUrl = [ new URL('../../renderer/app-shell.tsx', import.meta.url), diff --git a/apps/desktop/src/main/__tests__/workhub-session-resolver-port.test.ts b/apps/desktop/src/main/__tests__/workhub-session-resolver-port.test.ts index 6e9b2305df..3ef190f5b1 100644 --- a/apps/desktop/src/main/__tests__/workhub-session-resolver-port.test.ts +++ b/apps/desktop/src/main/__tests__/workhub-session-resolver-port.test.ts @@ -22,7 +22,7 @@ import test from 'node:test'; import type { WorkHubSessionResolution, WorkHubSessionResolver, -} from '../../renderer/application/contracts/workhub-request-intent.js'; +} from '@maka/core/workhub-session-resolver'; import { createWorkHubRoutePolicy } from '../../renderer/workhub-route-policy.js'; const routable = (sessionId: string, sessionName: string) => ({ diff --git a/apps/desktop/src/main/__tests__/workhub-surface-flow.test.ts b/apps/desktop/src/main/__tests__/workhub-surface-flow.test.ts index 8d9166794e..44f2db3830 100644 --- a/apps/desktop/src/main/__tests__/workhub-surface-flow.test.ts +++ b/apps/desktop/src/main/__tests__/workhub-surface-flow.test.ts @@ -35,14 +35,14 @@ import { workHubSurfaceFailure, workHubSubmissionClearsDraft, } from '../../renderer/workhub-surface.js'; +import { createWorkHubController } from '../../renderer/workhub-controller.js'; import { - createWorkHubController, WORKHUB_ROUTING_STRATEGY_ID, type WorkHubController, type WorkHubCoordinationTurn, type WorkHubDelegationExecutionState, type WorkHubSubmitInput, -} from '../../renderer/workhub-controller.js'; +} from '../../renderer/application/contracts/workhub.js'; import { WorkHubSendLease } from '../../renderer/workhub-send-lease.js'; import { createDesktopWorkHubSessionPort, diff --git a/apps/desktop/src/renderer/application/contracts/workhub-request-intent.ts b/apps/desktop/src/renderer/application/contracts/workhub-request-intent.ts deleted file mode 100644 index 8164ee8e5c..0000000000 --- a/apps/desktop/src/renderer/application/contracts/workhub-request-intent.ts +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -export { - readWorkHubRequestIntent, - workHubCorrectionAdmitsReference, -} from '@maka/core/workhub-creation-intent'; -export type { WorkHubRequestIntent } from '@maka/core/workhub-creation-intent'; -export { createExactNameSessionResolver } from '@maka/core/workhub-session-resolver'; -export type { - WorkHubResolverSession, - WorkHubSessionResolution, - WorkHubSessionResolver, -} from '@maka/core/workhub-session-resolver'; diff --git a/apps/desktop/src/renderer/application/contracts/workhub.ts b/apps/desktop/src/renderer/application/contracts/workhub.ts new file mode 100644 index 0000000000..5e16cabd9c --- /dev/null +++ b/apps/desktop/src/renderer/application/contracts/workhub.ts @@ -0,0 +1,282 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +/** + * The renderer-side WorkHub interface. + * + * WorkHub is a projection and routing surface over ordinary Sessions. Session + * and Runtime remain authoritative for transcript, execution, state, + * permissions, interactions, and recovery. Desktop adapters implement the two + * ports below; the controller coordinates them without becoming their contract. + */ + +import type { + OperationError, + WorkHubCoordinationActInput, + WorkHubCoordinationActResult, + WorkHubCoordinationCandidatesResult, +} from '@maka/runtime-host/protocol'; + +export type WorkHubRouteEvidence = + | 'explicit_target' + | 'exact_session_name' + | 'route_correction' + | 'core_entity' + | 'recent_focus'; + +export type WorkHubStopClarificationReason = + | 'stop_target_required' + | 'stop_target_ambiguous' + | 'stop_target_unavailable'; + +/** A Host refusal that callers may handle without depending on an adapter. */ +export class WorkHubCoordinationFailure extends Error { + constructor( + readonly code: OperationError<'workhub.coordination.act'>['code'], + message: string, + ) { + super(message); + this.name = 'WorkHubCoordinationFailure'; + } +} + +export interface WorkHubSessionTarget { + sessionId: string; +} + +export type WorkHubSessionState = + | 'active' + | 'running' + | 'waiting_for_user' + | 'blocked' + | 'aborted'; + +export interface WorkHubSessionFacts { + target: WorkHubSessionTarget; + projectName: string; + sessionName: string; + kind: 'ordinary' | 'internal' | 'subagent'; + archived: boolean; + state: WorkHubSessionState; + /** Authoritative live Turn IDs when the Session catalog provides them. */ + runningTurnIds?: readonly string[]; + latestResult?: string; + updatedAt: number; +} + +export type WorkHubSessionSummary = Omit; + +export type WorkHubProjectedTurnState = 'running' | 'completed' | 'aborted' | 'failed'; + +export type WorkHubDelegationExecutionState = + | 'accepted' + | 'running' + | 'waiting_for_user' + | 'completed' + | 'failed' + | 'aborted' + | 'recovering'; + +export interface WorkHubDelegationReference { + readonly delegationId: string; + readonly targetSessionId: string; + /** Stable delegated work identity; targetTurnId is only its admission location. */ + readonly targetMessageId: string; + readonly targetTurnId: string; +} + +export interface WorkHubDelegationFeedback { + readonly delegationId: string; + readonly state: WorkHubDelegationExecutionState; +} + +export interface WorkHubProjectedTurn { + messageId: string; + target: WorkHubSessionTarget; + turnId: string; + text: string; + state: WorkHubProjectedTurnState; + result?: string; + updatedAt: number; +} + +export interface WorkHubCoordinationTurn { + messageId: string; + turnId: string; + text: string; + state: WorkHubProjectedTurnState; + result?: string; + assignment?: { + readonly actionId: string; + readonly delegationId: string; + readonly targetSessionId: string; + readonly targetSessionName: string; + readonly targetMessageId: string; + readonly targetTurnId: string; + readonly feedbackState: WorkHubDelegationExecutionState; + readonly linkState: WorkHubDelegationLinkState; + readonly createdNew?: true; + }; + stop?: { + readonly targetSessionId: string; + readonly targetSessionName: string; + readonly outcome?: Extract['outcome']; + }; + updatedAt: number; +} + +export type WorkHubDelegationLinkState = 'active' | 'superseded' | 'aborted' | 'stopped'; + +/** Unbounded, rebuildable linkage state kept separate from the bounded timeline. */ +export interface WorkHubActiveDelegation { + readonly actionId: string; + readonly targetSessionId: string; + readonly sequence: number; +} + +const WORKHUB_TIMELINE_TEXT_LIMIT = 600; + +/** Applies the bounded-text invariant shared by both projection adapters. */ +export function boundedWorkHubTimelineText(value: string): string { + const text = value.trim(); + const chars = Array.from(text); + return chars.length <= WORKHUB_TIMELINE_TEXT_LIMIT + ? text + : `${chars.slice(0, WORKHUB_TIMELINE_TEXT_LIMIT - 1).join('')}…`; +} + +export interface WorkHubProjection { + sessions: WorkHubSessionSummary[]; + turns: WorkHubProjectedTurn[]; +} + +export interface WorkHubSubmitInput { + requestId: string; + text: string; + retryAction?: true; + explicitTarget?: WorkHubSessionTarget; + correction?: WorkHubCorrectionContext; +} + +export interface WorkHubCorrectionContext { + from: WorkHubSessionTarget; + sourceActionId: string; +} + +export interface WorkHubReadInput { + focus?: WorkHubSessionTarget; +} + +export const WORKHUB_ROUTING_STRATEGY_ID = 'wh-r2.4-session-context-continuity' as const; +export type WorkHubRoutingStrategyId = typeof WORKHUB_ROUTING_STRATEGY_ID; + +export type WorkHubSubmission = ( + | { + kind: 'submitted'; + requestId: string; + target: WorkHubSessionTarget; + turnId: string; + steered?: true; + evidence: WorkHubRouteEvidence | 'new_session'; + correctedFrom?: WorkHubSessionTarget; + } + | { + kind: 'clarification'; + requestId: string; + text: string; + options: Array>; + reason?: 'ambiguous_command' | WorkHubStopClarificationReason; + correction?: WorkHubCorrectionContext; + } + | { + kind: 'discussion'; + requestId: string; + text: string; + } + | { + kind: 'waiting'; + requestId: string; + text: string; + target: WorkHubSessionTarget; + } + | { + kind: 'stop'; + requestId: string; + target: WorkHubSessionTarget; + outcome: Extract['outcome']; + targetTurnId?: string; + } +) & { strategyId: WorkHubRoutingStrategyId }; + +/** + * Read-only access to ordinary Session facts. The Desktop bridge is the + * production adapter; controller tests use an in-memory adapter. + */ +export interface WorkHubSessionPort { + list(): Promise; + /** Rebuilds a bounded recent conversation from authoritative transcripts. */ + recentTurns(targets: readonly WorkHubSessionTarget[]): Promise; + /** Rebuilds target-owned execution facts for durable delegation links. */ + delegationFeedback( + references: readonly WorkHubDelegationReference[], + ): Promise; + /** Returns rebuildable routing evidence from the authoritative Session log. */ + routingEvidence( + targets: readonly WorkHubSessionTarget[], + ): Promise>; + subscribe(handler: () => void): () => void; +} + +/** + * Access to Coordination Session facts and the Runtime Host Action Gate. Only + * `act` can request an effect, and the Host remains the admission authority. + */ +export interface WorkHubCoordinationPort { + open( + handler: ( + turns: readonly WorkHubCoordinationTurn[], + activeDelegations: readonly WorkHubActiveDelegation[], + ) => void, + onError: (error: unknown) => void, + ): Promise<{ close(): Promise }>; + record(input: { + turnId: string; + userText: string; + assistantText: string; + }): Promise<{ turnId: string }>; + candidates(): Promise; + act(input: Omit): Promise; +} + +export interface WorkHubController { + read(input?: WorkHubReadInput): Promise; + submit(input: WorkHubSubmitInput): Promise; + openConversation( + handler: (turns: readonly WorkHubCoordinationTurn[]) => void, + onError: (error: unknown) => void, + ): Promise<{ close(): Promise }>; + recordConversationTurn(input: { + turnId: string; + userText: string; + assistantText: string; + disposition?: 'clarify' | 'summary'; + }): Promise<{ turnId: string }>; + subscribe(handler: () => void): () => void; + resetVisitContext(): void; +} diff --git a/apps/desktop/src/renderer/workhub-controller.ts b/apps/desktop/src/renderer/workhub-controller.ts index f900300483..cf4e315154 100644 --- a/apps/desktop/src/renderer/workhub-controller.ts +++ b/apps/desktop/src/renderer/workhub-controller.ts @@ -25,260 +25,27 @@ import { createWorkHubRoutePolicy, - type WorkHubRouteEvidence, - type WorkHubStopClarificationReason, } from './workhub-route-policy.js'; import type { - OperationError, WorkHubCoordinationActInput, WorkHubCoordinationActResult, - WorkHubCoordinationCandidatesResult, } from '@maka/runtime-host/protocol'; - -/** - * A Host operation the Coordination port could not complete. It lives beside - * the port interface rather than beside its Desktop implementation, so a - * caller can tell a refusal from a fault without depending on the adapter. - */ -export class WorkHubCoordinationFailure extends Error { - constructor( - readonly code: OperationError<'workhub.coordination.act'>['code'], - message: string, - ) { - super(message); - this.name = 'WorkHubCoordinationFailure'; - } -} - -export interface WorkHubSessionTarget { - sessionId: string; -} - -export type WorkHubSessionState = - | 'active' - | 'running' - | 'waiting_for_user' - | 'blocked' - | 'aborted'; - -export interface WorkHubSessionFacts { - target: WorkHubSessionTarget; - projectName: string; - sessionName: string; - kind: 'ordinary' | 'internal' | 'subagent'; - archived: boolean; - state: WorkHubSessionState; - /** Authoritative live Turn IDs when the Session catalog provides them. */ - runningTurnIds?: readonly string[]; - latestResult?: string; - updatedAt: number; -} - -export type WorkHubSessionSummary = Omit; - -export type WorkHubProjectedTurnState = 'running' | 'completed' | 'aborted' | 'failed'; - -export type WorkHubDelegationExecutionState = - | 'accepted' - | 'running' - | 'waiting_for_user' - | 'completed' - | 'failed' - | 'aborted' - | 'recovering'; - -export interface WorkHubDelegationReference { - readonly delegationId: string; - readonly targetSessionId: string; - /** Stable delegated work identity; targetTurnId is only its admission location. */ - readonly targetMessageId: string; - readonly targetTurnId: string; -} - -export interface WorkHubDelegationFeedback { - readonly delegationId: string; - readonly state: WorkHubDelegationExecutionState; -} - -export interface WorkHubProjectedTurn { - messageId: string; - target: WorkHubSessionTarget; - turnId: string; - text: string; - state: WorkHubProjectedTurnState; - result?: string; - updatedAt: number; -} - -export interface WorkHubCoordinationTurn { - messageId: string; - turnId: string; - text: string; - state: WorkHubProjectedTurnState; - result?: string; - assignment?: { - readonly actionId: string; - readonly delegationId: string; - readonly targetSessionId: string; - readonly targetSessionName: string; - readonly targetMessageId: string; - readonly targetTurnId: string; - readonly feedbackState: WorkHubDelegationExecutionState; - readonly linkState: WorkHubDelegationLinkState; - readonly createdNew?: true; - }; - stop?: { - readonly targetSessionId: string; - readonly targetSessionName: string; - readonly outcome?: Extract['outcome']; - }; - updatedAt: number; -} - -export type WorkHubDelegationLinkState = 'active' | 'superseded' | 'aborted' | 'stopped'; - -/** Unbounded, rebuildable linkage state kept separate from the bounded timeline. */ -export interface WorkHubActiveDelegation { - readonly actionId: string; - readonly targetSessionId: string; - readonly sequence: number; -} - -const WORKHUB_TIMELINE_TEXT_LIMIT = 600; - -export function boundedWorkHubTimelineText(value: string): string { - const text = value.trim(); - const chars = Array.from(text); - return chars.length <= WORKHUB_TIMELINE_TEXT_LIMIT - ? text - : `${chars.slice(0, WORKHUB_TIMELINE_TEXT_LIMIT - 1).join('')}…`; -} - -export interface WorkHubProjection { - sessions: WorkHubSessionSummary[]; - turns: WorkHubProjectedTurn[]; -} - -export interface WorkHubSubmitInput { - requestId: string; - text: string; - retryAction?: true; - explicitTarget?: WorkHubSessionTarget; - correction?: WorkHubCorrectionContext; -} - -export interface WorkHubCorrectionContext { - from: WorkHubSessionTarget; - sourceActionId: string; -} - -export interface WorkHubReadInput { - focus?: WorkHubSessionTarget; -} - -export const WORKHUB_ROUTING_STRATEGY_ID = 'wh-r2.4-session-context-continuity' as const; -export type WorkHubRoutingStrategyId = typeof WORKHUB_ROUTING_STRATEGY_ID; - -export type WorkHubSubmission = ( - | { - kind: 'submitted'; - requestId: string; - target: WorkHubSessionTarget; - turnId: string; - steered?: true; - evidence: WorkHubRouteEvidence | 'new_session'; - correctedFrom?: WorkHubSessionTarget; - } - | { - kind: 'clarification'; - requestId: string; - text: string; - options: Array>; - reason?: 'ambiguous_command' | WorkHubStopClarificationReason; - correction?: WorkHubCorrectionContext; - } - | { - kind: 'discussion'; - requestId: string; - text: string; - } - | { - kind: 'waiting'; - requestId: string; - text: string; - target: WorkHubSessionTarget; - } - | { - kind: 'stop'; - requestId: string; - target: WorkHubSessionTarget; - outcome: Extract['outcome']; - targetTurnId?: string; - } -) & { strategyId: WorkHubRoutingStrategyId }; - -/** - * Internal seam. The renderer bridge is the production adapter; interface - * tests use an in-memory adapter. - */ -export interface WorkHubSessionPort { - list(): Promise; - /** - * Rebuilds a bounded recent conversation from the authoritative Session - * transcripts. Missing transcripts are omitted rather than copied elsewhere. - */ - recentTurns(targets: readonly WorkHubSessionTarget[]): Promise; - /** - * Rebuilds exact target-Turn execution facts for durable delegation links. - * The target Session remains authoritative; results are read-only and may - * conservatively report `recovering` while that authority is unavailable. - */ - delegationFeedback( - references: readonly WorkHubDelegationReference[], - ): Promise; - /** - * Returns rebuildable routing evidence read from the authoritative Session - * log. Implementations must not persist a second writable copy of it. - */ - routingEvidence( - targets: readonly WorkHubSessionTarget[], - ): Promise>; - subscribe(handler: () => void): () => void; -} - -export interface WorkHubCoordinationPort { - open( - handler: ( - turns: readonly WorkHubCoordinationTurn[], - activeDelegations: readonly WorkHubActiveDelegation[], - ) => void, - onError: (error: unknown) => void, - ): Promise<{ close(): Promise }>; - record(input: { - turnId: string; - userText: string; - assistantText: string; - }): Promise<{ turnId: string }>; - candidates(): Promise; - act(input: Omit): Promise; -} - -export interface WorkHubController { - read(input?: WorkHubReadInput): Promise; - submit(input: WorkHubSubmitInput): Promise; - openConversation( - handler: (turns: readonly WorkHubCoordinationTurn[]) => void, - onError: (error: unknown) => void, - ): Promise<{ close(): Promise }>; - recordConversationTurn(input: { - turnId: string; - userText: string; - assistantText: string; - disposition?: 'clarify' | 'summary'; - }): Promise<{ turnId: string }>; - subscribe(handler: () => void): () => void; - resetVisitContext(): void; -} +import { + WORKHUB_ROUTING_STRATEGY_ID, + WorkHubCoordinationFailure, + type WorkHubActiveDelegation, + type WorkHubController, + type WorkHubCoordinationPort, + type WorkHubCoordinationTurn, + type WorkHubCorrectionContext, + type WorkHubDelegationFeedback, + type WorkHubRouteEvidence, + type WorkHubSessionFacts, + type WorkHubSessionPort, + type WorkHubSessionTarget, + type WorkHubSubmission, + type WorkHubSubmitInput, +} from './application/contracts/workhub.js'; export function createWorkHubController(deps: { sessions: WorkHubSessionPort; diff --git a/apps/desktop/src/renderer/workhub-coordination-port.ts b/apps/desktop/src/renderer/workhub-coordination-port.ts index 114fed8532..81922412db 100644 --- a/apps/desktop/src/renderer/workhub-coordination-port.ts +++ b/apps/desktop/src/renderer/workhub-coordination-port.ts @@ -29,7 +29,7 @@ import type { WorkHubCoordinationTurn, WorkHubActiveDelegation, WorkHubProjectedTurnState, -} from './workhub-controller.js'; +} from './application/contracts/workhub.js'; import type { WorkHubCoordinationActInput, WorkHubCoordinationActResult, @@ -37,9 +37,10 @@ import type { OperationOutcome, OperationError, } from '@maka/runtime-host/protocol'; -import { boundedWorkHubTimelineText, WorkHubCoordinationFailure } from './workhub-controller.js'; - -export { WorkHubCoordinationFailure }; +import { + boundedWorkHubTimelineText, + WorkHubCoordinationFailure, +} from './application/contracts/workhub.js'; import type { WorkHubDesktopTranscriptBridge } from './workhub-session-port.js'; const WORKHUB_COORDINATION_TURN_LIMIT = 40; diff --git a/apps/desktop/src/renderer/workhub-route-policy.ts b/apps/desktop/src/renderer/workhub-route-policy.ts index af2960c94f..227bafa9ee 100644 --- a/apps/desktop/src/renderer/workhub-route-policy.ts +++ b/apps/desktop/src/renderer/workhub-route-policy.ts @@ -18,13 +18,19 @@ */ import { - createExactNameSessionResolver, readWorkHubRequestIntent, workHubCorrectionAdmitsReference, type WorkHubRequestIntent, +} from '@maka/core/workhub-creation-intent'; +import { + createExactNameSessionResolver, type WorkHubResolverSession, type WorkHubSessionResolver, -} from './application/contracts/workhub-request-intent.js'; +} from '@maka/core/workhub-session-resolver'; +import type { + WorkHubRouteEvidence, + WorkHubStopClarificationReason, +} from './application/contracts/workhub.js'; interface WorkHubRouteTarget { sessionId: string; @@ -38,13 +44,6 @@ interface WorkHubRoutableSession { updatedAt: number; } -export type WorkHubRouteEvidence = - | 'explicit_target' - | 'exact_session_name' - | 'route_correction' - | 'core_entity' - | 'recent_focus'; - export type WorkHubRouteDecision = | { kind: 'target'; @@ -73,14 +72,6 @@ export type WorkHubRouteDecision = * reload, a reconnect. So a resolved reference submits, and a Session with * nothing to stop is refused by the Gate. */ -export type WorkHubStopClarificationReason = - /** The stop names no safe target of its own — a pronoun or a bare noun. */ - | 'stop_target_required' - /** The stop names more than one existing Session. */ - | 'stop_target_ambiguous' - /** The Host refused the stop; its conflict is the whole answer. */ - | 'stop_target_unavailable'; - /** * A stop clarification never offers route options. Choosing one re-sends the * original text as work, and stop-shaped text is exactly what must not be diff --git a/apps/desktop/src/renderer/workhub-session-port.ts b/apps/desktop/src/renderer/workhub-session-port.ts index 9bf586d3bd..6425d03d1e 100644 --- a/apps/desktop/src/renderer/workhub-session-port.ts +++ b/apps/desktop/src/renderer/workhub-session-port.ts @@ -36,8 +36,8 @@ import type { WorkHubSessionPort, WorkHubSessionState, WorkHubSessionTarget, -} from './workhub-controller.js'; -import { boundedWorkHubTimelineText } from './workhub-controller.js'; +} from './application/contracts/workhub.js'; +import { boundedWorkHubTimelineText } from './application/contracts/workhub.js'; export interface WorkHubDesktopSession { id: string; diff --git a/apps/desktop/src/renderer/workhub-surface.tsx b/apps/desktop/src/renderer/workhub-surface.tsx index 8a7cf93ad2..3a6ecd40da 100644 --- a/apps/desktop/src/renderer/workhub-surface.tsx +++ b/apps/desktop/src/renderer/workhub-surface.tsx @@ -35,12 +35,12 @@ import type { WorkHubSessionSummary, WorkHubSubmission, WorkHubSubmitInput, -} from './workhub-controller.js'; +} from './application/contracts/workhub.js'; import { WorkHubSendLease, type WorkHubSendAttempt, } from './workhub-send-lease.js'; -import { WorkHubCoordinationFailure } from './workhub-coordination-port.js'; +import { WorkHubCoordinationFailure } from './application/contracts/workhub.js'; export interface WorkHubConversationTurn { requestId: string; From 08deecd70cc5086c2c32f2686137bd21d2f5c203 Mon Sep 17 00:00:00 2001 From: 404ARE <936233544@qq.com> Date: Sat, 5 Sep 2026 10:51:10 +0800 Subject: [PATCH 2/2] fix(workhub): preserve renderer contract seams --- apps/desktop/renderer-architecture.json | 4 +-- .../workhub-session-resolver-port.test.ts | 2 +- .../contracts/workhub-action-intent.ts | 25 ++++++++++++++++++ .../contracts/workhub-session-resolver.ts | 26 +++++++++++++++++++ .../src/renderer/workhub-route-policy.ts | 4 +-- 5 files changed, 56 insertions(+), 5 deletions(-) create mode 100644 apps/desktop/src/renderer/application/contracts/workhub-action-intent.ts create mode 100644 apps/desktop/src/renderer/application/contracts/workhub-session-resolver.ts diff --git a/apps/desktop/renderer-architecture.json b/apps/desktop/renderer-architecture.json index 6fc52f373e..81dbb13605 100644 --- a/apps/desktop/renderer-architecture.json +++ b/apps/desktop/renderer-architecture.json @@ -4580,8 +4580,8 @@ "unresolvedDependencies": 0, "actionFactories": [], "dependencyPaths": { - "@maka/core/workhub-creation-intent": 1, - "@maka/core/workhub-session-resolver": 1 + "./application/contracts/workhub-action-intent.js": 1, + "./application/contracts/workhub-session-resolver.js": 1 } }, "src/renderer/workhub-send-lease.ts": { diff --git a/apps/desktop/src/main/__tests__/workhub-session-resolver-port.test.ts b/apps/desktop/src/main/__tests__/workhub-session-resolver-port.test.ts index 3ef190f5b1..219ac233f2 100644 --- a/apps/desktop/src/main/__tests__/workhub-session-resolver-port.test.ts +++ b/apps/desktop/src/main/__tests__/workhub-session-resolver-port.test.ts @@ -22,7 +22,7 @@ import test from 'node:test'; import type { WorkHubSessionResolution, WorkHubSessionResolver, -} from '@maka/core/workhub-session-resolver'; +} from '../../renderer/application/contracts/workhub-session-resolver.js'; import { createWorkHubRoutePolicy } from '../../renderer/workhub-route-policy.js'; const routable = (sessionId: string, sessionName: string) => ({ diff --git a/apps/desktop/src/renderer/application/contracts/workhub-action-intent.ts b/apps/desktop/src/renderer/application/contracts/workhub-action-intent.ts new file mode 100644 index 0000000000..12292b16e5 --- /dev/null +++ b/apps/desktop/src/renderer/application/contracts/workhub-action-intent.ts @@ -0,0 +1,25 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +/** Renderer seam for the shared, pure WorkHub Action Intent module. */ +export { + readWorkHubRequestIntent, + workHubCorrectionAdmitsReference, +} from '@maka/core/workhub-creation-intent'; +export type { WorkHubRequestIntent } from '@maka/core/workhub-creation-intent'; diff --git a/apps/desktop/src/renderer/application/contracts/workhub-session-resolver.ts b/apps/desktop/src/renderer/application/contracts/workhub-session-resolver.ts new file mode 100644 index 0000000000..344cf59d1e --- /dev/null +++ b/apps/desktop/src/renderer/application/contracts/workhub-session-resolver.ts @@ -0,0 +1,26 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +/** Renderer seam for recalling existing Sessions without choosing an action. */ +export { createExactNameSessionResolver } from '@maka/core/workhub-session-resolver'; +export type { + WorkHubResolverSession, + WorkHubSessionResolution, + WorkHubSessionResolver, +} from '@maka/core/workhub-session-resolver'; diff --git a/apps/desktop/src/renderer/workhub-route-policy.ts b/apps/desktop/src/renderer/workhub-route-policy.ts index 227bafa9ee..abf9d12d7e 100644 --- a/apps/desktop/src/renderer/workhub-route-policy.ts +++ b/apps/desktop/src/renderer/workhub-route-policy.ts @@ -21,12 +21,12 @@ import { readWorkHubRequestIntent, workHubCorrectionAdmitsReference, type WorkHubRequestIntent, -} from '@maka/core/workhub-creation-intent'; +} from './application/contracts/workhub-action-intent.js'; import { createExactNameSessionResolver, type WorkHubResolverSession, type WorkHubSessionResolver, -} from '@maka/core/workhub-session-resolver'; +} from './application/contracts/workhub-session-resolver.js'; import type { WorkHubRouteEvidence, WorkHubStopClarificationReason,