Files
agent-radar 2ed3415fb7
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
源码快照 @ 2026-09-01 (source-only, 剔除非源码素材)
2026-09-01 20:17:27 +08:00

124 lines
4.2 KiB
TypeScript

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));
}