

























@@ -186,66 +186,81 @@ export async function projectSessionsPatchEntry(params: {
186186delete next.displayName;
187187}
188188189-if ("spawnedBy" in patch) {
190-const raw = patch.spawnedBy;
189+type PatchError = ReturnType<typeof invalid> | null;
190+const checkSpawnLineage = (field: string): PatchError =>
191+supportsSpawnLineage(storeKey)
192+ ? null
193+ : invalid(`${field} is only supported for subagent:* or acp:* sessions`);
194+const applyImmutableString = (
195+field: "spawnedBy" | "spawnedWorkspaceDir" | "spawnedCwd",
196+checkLineageBeforeEmpty: boolean,
197+): PatchError => {
198+if (!(field in patch)) {
199+return null;
200+}
201+const raw = patch[field];
191202if (raw === null) {
192-if (existing?.spawnedBy) {
193-return invalid("spawnedBy cannot be cleared once set");
194-}
195-} else if (raw !== undefined) {
196-const trimmed = normalizeOptionalString(raw) ?? "";
197-if (!trimmed) {
198-return invalid("invalid spawnedBy: empty");
199-}
200-if (!supportsSpawnLineage(storeKey)) {
201-return invalid("spawnedBy is only supported for subagent:* or acp:* sessions");
202-}
203-if (existing?.spawnedBy && existing.spawnedBy !== trimmed) {
204-return invalid("spawnedBy cannot be changed once set");
205-}
206-next.spawnedBy = trimmed;
203+return existing?.[field] ? invalid(`${field} cannot be cleared once set`) : null;
207204}
208-}
209-210-if ("spawnedWorkspaceDir" in patch) {
211-const raw = patch.spawnedWorkspaceDir;
205+if (raw === undefined) {
206+return null;
207+}
208+const earlyLineage = checkLineageBeforeEmpty ? checkSpawnLineage(field) : null;
209+if (earlyLineage) {
210+return earlyLineage;
211+}
212+const trimmed = normalizeOptionalString(raw) ?? "";
213+if (!trimmed) {
214+return invalid(`invalid ${field}: empty`);
215+}
216+const lateLineage = checkLineageBeforeEmpty ? null : checkSpawnLineage(field);
217+if (lateLineage) {
218+return lateLineage;
219+}
220+if (existing?.[field] && existing[field] !== trimmed) {
221+return invalid(`${field} cannot be changed once set`);
222+}
223+next[field] = trimmed;
224+return null;
225+};
226+const applyImmutableNormalized = <T extends "subagentRole" | "subagentControlScope">(
227+field: T,
228+normalize: (raw: string) => NonNullable<SessionEntry[T]> | undefined,
229+invalidMessage: string,
230+): PatchError => {
231+if (!(field in patch)) {
232+return null;
233+}
234+const raw = patch[field];
212235if (raw === null) {
213-if (existing?.spawnedWorkspaceDir) {
214-return invalid("spawnedWorkspaceDir cannot be cleared once set");
215-}
216-} else if (raw !== undefined) {
217-if (!supportsSpawnLineage(storeKey)) {
218-return invalid("spawnedWorkspaceDir is only supported for subagent:* or acp:* sessions");
219-}
220-const trimmed = normalizeOptionalString(raw) ?? "";
221-if (!trimmed) {
222-return invalid("invalid spawnedWorkspaceDir: empty");
223-}
224-if (existing?.spawnedWorkspaceDir && existing.spawnedWorkspaceDir !== trimmed) {
225-return invalid("spawnedWorkspaceDir cannot be changed once set");
226-}
227-next.spawnedWorkspaceDir = trimmed;
236+return existing?.[field] ? invalid(`${field} cannot be cleared once set`) : null;
228237}
229-}
238+if (raw === undefined) {
239+return null;
240+}
241+const lineage = checkSpawnLineage(field);
242+if (lineage) {
243+return lineage;
244+}
245+const normalized = normalize(raw);
246+if (!normalized) {
247+return invalid(invalidMessage);
248+}
249+if (existing?.[field] && existing[field] !== normalized) {
250+return invalid(`${field} cannot be changed once set`);
251+}
252+next[field] = normalized;
253+return null;
254+};
230255231-if ("spawnedCwd" in patch) {
232-const raw = patch.spawnedCwd;
233-if (raw === null) {
234-if (existing?.spawnedCwd) {
235-return invalid("spawnedCwd cannot be cleared once set");
236-}
237-} else if (raw !== undefined) {
238-if (!supportsSpawnLineage(storeKey)) {
239-return invalid("spawnedCwd is only supported for subagent:* or acp:* sessions");
240-}
241-const trimmed = normalizeOptionalString(raw) ?? "";
242-if (!trimmed) {
243-return invalid("invalid spawnedCwd: empty");
244-}
245-if (existing?.spawnedCwd && existing.spawnedCwd !== trimmed) {
246-return invalid("spawnedCwd cannot be changed once set");
247-}
248-next.spawnedCwd = trimmed;
256+for (const fieldParams of [
257+{ field: "spawnedBy" as const, checkLineageBeforeEmpty: false },
258+{ field: "spawnedWorkspaceDir" as const, checkLineageBeforeEmpty: true },
259+{ field: "spawnedCwd" as const, checkLineageBeforeEmpty: true },
260+]) {
261+const result = applyImmutableString(fieldParams.field, fieldParams.checkLineageBeforeEmpty);
262+if (result) {
263+return result;
249264}
250265}
251266@@ -271,45 +286,25 @@ export async function projectSessionsPatchEntry(params: {
271286}
272287}
273288274-if ("subagentRole" in patch) {
275-const raw = patch.subagentRole;
276-if (raw === null) {
277-if (existing?.subagentRole) {
278-return invalid("subagentRole cannot be cleared once set");
279-}
280-} else if (raw !== undefined) {
281-if (!supportsSpawnLineage(storeKey)) {
282-return invalid("subagentRole is only supported for subagent:* or acp:* sessions");
283-}
284-const normalized = normalizeSubagentRole(raw);
285-if (!normalized) {
286-return invalid('invalid subagentRole (use "orchestrator" or "leaf")');
287-}
288-if (existing?.subagentRole && existing.subagentRole !== normalized) {
289-return invalid("subagentRole cannot be changed once set");
290-}
291-next.subagentRole = normalized;
292-}
293-}
294-295-if ("subagentControlScope" in patch) {
296-const raw = patch.subagentControlScope;
297-if (raw === null) {
298-if (existing?.subagentControlScope) {
299-return invalid("subagentControlScope cannot be cleared once set");
300-}
301-} else if (raw !== undefined) {
302-if (!supportsSpawnLineage(storeKey)) {
303-return invalid("subagentControlScope is only supported for subagent:* or acp:* sessions");
304-}
305-const normalized = normalizeSubagentControlScope(raw);
306-if (!normalized) {
307-return invalid('invalid subagentControlScope (use "children" or "none")');
308-}
309-if (existing?.subagentControlScope && existing.subagentControlScope !== normalized) {
310-return invalid("subagentControlScope cannot be changed once set");
311-}
312-next.subagentControlScope = normalized;
289+for (const fieldParams of [
290+{
291+field: "subagentRole" as const,
292+normalize: normalizeSubagentRole,
293+invalidMessage: 'invalid subagentRole (use "orchestrator" or "leaf")',
294+},
295+{
296+field: "subagentControlScope" as const,
297+normalize: normalizeSubagentControlScope,
298+invalidMessage: 'invalid subagentControlScope (use "children" or "none")',
299+},
300+]) {
301+const result = applyImmutableNormalized(
302+fieldParams.field,
303+fieldParams.normalize,
304+fieldParams.invalidMessage,
305+);
306+if (result) {
307+return result;
313308}
314309}
315310此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。