源码快照 @ 2026-09-01 (source-only, 剔除非源码素材)
Publish Claude Code Binaries / Stage and publish binaries (push) Canceled after 0s
Publish Tutti App Runtime / Build darwin-arm64 (push) Canceled after 0s
Publish Tutti App Runtime / Build darwin-amd64 (push) Canceled after 0s
Publish Tutti App Runtime / Build linux-amd64 (push) Canceled after 0s
Publish Tutti App Runtime / Build windows-amd64 (push) Canceled after 0s
Sync Tutti Agent Skills / sync (push) Canceled after 0s
Windows Agent Adapters / Test Windows agent adapters (push) Canceled after 0s
Windows Daemon Adapters / Test Windows daemon adapters (push) Canceled after 0s
Publish Tutti App Runtime / Publish runtime catalog (push) Canceled after 0s
Publish Claude Code Binaries / Stage and publish binaries (push) Canceled after 0s
Publish Tutti App Runtime / Build darwin-arm64 (push) Canceled after 0s
Publish Tutti App Runtime / Build darwin-amd64 (push) Canceled after 0s
Publish Tutti App Runtime / Build linux-amd64 (push) Canceled after 0s
Publish Tutti App Runtime / Build windows-amd64 (push) Canceled after 0s
Sync Tutti Agent Skills / sync (push) Canceled after 0s
Windows Agent Adapters / Test Windows agent adapters (push) Canceled after 0s
Windows Daemon Adapters / Test Windows daemon adapters (push) Canceled after 0s
Publish Tutti App Runtime / Publish runtime catalog (push) Canceled after 0s
This commit is contained in:
@@ -0,0 +1,123 @@
|
||||
import type { TuttiModePlanTaskAssignmentInput } from "../workspaceWorkflowRuntime";
|
||||
import type { TuttiModePlanPanelTaskViewModel } from "./tuttiModePlanPanelProjection";
|
||||
|
||||
/**
|
||||
* One task's in-panel assignment edits. `undefined` means "untouched — keep
|
||||
* the plan document value"; an empty string is an explicit clear that the
|
||||
* daemon persists as such.
|
||||
*/
|
||||
export interface TuttiModePlanTaskAssignmentDraft {
|
||||
agentTargetId?: string;
|
||||
modelPlanId?: string;
|
||||
model?: string;
|
||||
permissionModeId?: string;
|
||||
reasoningEffort?: string;
|
||||
/** Per-task parallel opt-in; undefined keeps the plan document value. */
|
||||
parallelizable?: boolean;
|
||||
/** Per-task acceptance bypass; undefined keeps the plan document value. */
|
||||
autoAccept?: boolean;
|
||||
}
|
||||
|
||||
export type TuttiModePlanTaskAssignmentDrafts = Readonly<
|
||||
Record<string, TuttiModePlanTaskAssignmentDraft>
|
||||
>;
|
||||
|
||||
/**
|
||||
* Applies one field edit. Changing the Agent invalidates the dependent
|
||||
* selections (plan, model, permission mode, reasoning effort) because their
|
||||
* option catalogs are agent-scoped; they reset to an explicit clear so an
|
||||
* incompatible document value can never ride along silently. The parallel
|
||||
* opt-in is agent-independent and survives an agent change.
|
||||
*/
|
||||
export function mergeTaskAssignmentDraft(
|
||||
drafts: TuttiModePlanTaskAssignmentDrafts,
|
||||
taskId: string,
|
||||
patch: TuttiModePlanTaskAssignmentDraft
|
||||
): TuttiModePlanTaskAssignmentDrafts {
|
||||
const current = drafts[taskId] ?? {};
|
||||
const next: TuttiModePlanTaskAssignmentDraft =
|
||||
patch.agentTargetId !== undefined &&
|
||||
patch.agentTargetId !== current.agentTargetId
|
||||
? {
|
||||
agentTargetId: patch.agentTargetId,
|
||||
modelPlanId: "",
|
||||
model: "",
|
||||
permissionModeId: "",
|
||||
reasoningEffort: "",
|
||||
...(current.parallelizable !== undefined
|
||||
? { parallelizable: current.parallelizable }
|
||||
: {}),
|
||||
...(current.autoAccept !== undefined
|
||||
? { autoAccept: current.autoAccept }
|
||||
: {})
|
||||
}
|
||||
: { ...current, ...patch };
|
||||
return { ...drafts, [taskId]: next };
|
||||
}
|
||||
|
||||
/** The state a boolean toggle (parallel, auto-accept) displays: draft wins. */
|
||||
export function effectiveTaskFlag(
|
||||
draftValue: boolean | undefined,
|
||||
documentValue: boolean
|
||||
): boolean {
|
||||
return draftValue ?? documentValue;
|
||||
}
|
||||
|
||||
/** The value a selector should display: draft edit wins over document value. */
|
||||
export function effectiveTaskAssignmentValue(
|
||||
draftValue: string | undefined,
|
||||
documentValue: string | null
|
||||
): string {
|
||||
if (draftValue !== undefined) return draftValue;
|
||||
return documentValue ?? "";
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds the decision payload. Only touched tasks are included, and only
|
||||
* touched fields are sent; untouched fields stay null-equivalent (omitted) so
|
||||
* the daemon keeps the plan document value.
|
||||
*/
|
||||
export function taskAssignmentInputsFromDrafts(
|
||||
drafts: TuttiModePlanTaskAssignmentDrafts,
|
||||
tasks: readonly TuttiModePlanPanelTaskViewModel[]
|
||||
): TuttiModePlanTaskAssignmentInput[] {
|
||||
const knownTaskIds = new Set(tasks.map((task) => task.id));
|
||||
const inputs: TuttiModePlanTaskAssignmentInput[] = [];
|
||||
for (const task of tasks) {
|
||||
const draft = drafts[task.id];
|
||||
if (!draft) continue;
|
||||
const input: TuttiModePlanTaskAssignmentInput = { taskId: task.id };
|
||||
let touched = false;
|
||||
if (draft.agentTargetId !== undefined) {
|
||||
input.agentTargetId = draft.agentTargetId;
|
||||
touched = true;
|
||||
}
|
||||
if (draft.modelPlanId !== undefined) {
|
||||
input.modelPlanId = draft.modelPlanId;
|
||||
touched = true;
|
||||
}
|
||||
if (draft.model !== undefined) {
|
||||
input.model = draft.model;
|
||||
touched = true;
|
||||
}
|
||||
if (draft.permissionModeId !== undefined) {
|
||||
input.permissionModeId = draft.permissionModeId;
|
||||
touched = true;
|
||||
}
|
||||
if (draft.reasoningEffort !== undefined) {
|
||||
input.reasoningEffort = draft.reasoningEffort;
|
||||
touched = true;
|
||||
}
|
||||
if (draft.parallelizable !== undefined) {
|
||||
input.parallelizable = draft.parallelizable;
|
||||
touched = true;
|
||||
}
|
||||
if (draft.autoAccept !== undefined) {
|
||||
input.autoAccept = draft.autoAccept;
|
||||
touched = true;
|
||||
}
|
||||
if (touched) inputs.push(input);
|
||||
}
|
||||
// Drafts for tasks that vanished from the current revision are dropped.
|
||||
return inputs.filter((input) => knownTaskIds.has(input.taskId));
|
||||
}
|
||||
Reference in New Issue
Block a user