fix(server): restore auto metadata generation
This commit is contained in:
@@ -53,7 +53,6 @@ import { normalizeAgentSnapshot } from '@/utils/agent-snapshots'
|
|||||||
import { useDraftAgentCreateFlow } from '@/hooks/use-draft-agent-create-flow'
|
import { useDraftAgentCreateFlow } from '@/hooks/use-draft-agent-create-flow'
|
||||||
|
|
||||||
const EMPTY_PENDING_PERMISSIONS = new Map()
|
const EMPTY_PENDING_PERMISSIONS = new Map()
|
||||||
const MAX_INITIAL_AGENT_TITLE_CHARS = 60
|
|
||||||
const DRAFT_CAPABILITIES: AgentCapabilityFlags = {
|
const DRAFT_CAPABILITIES: AgentCapabilityFlags = {
|
||||||
supportsStreaming: true,
|
supportsStreaming: true,
|
||||||
supportsSessionPersistence: false,
|
supportsSessionPersistence: false,
|
||||||
@@ -98,22 +97,6 @@ function getValidMode(provider: AgentProvider | undefined, value: string | undef
|
|||||||
return modes.some((mode) => mode.id === value) ? value : undefined
|
return modes.some((mode) => mode.id === value) ? value : undefined
|
||||||
}
|
}
|
||||||
|
|
||||||
function deriveInitialAgentTitle(prompt: string): string | null {
|
|
||||||
const firstContentLine = prompt
|
|
||||||
.split(/\r?\n/)
|
|
||||||
.map((line) => line.trim())
|
|
||||||
.find((line) => line.length > 0)
|
|
||||||
if (!firstContentLine) {
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
const normalized = firstContentLine.replace(/\s+/g, " ").trim()
|
|
||||||
if (!normalized) {
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
const clamped = normalized.slice(0, MAX_INITIAL_AGENT_TITLE_CHARS).trim()
|
|
||||||
return clamped.length > 0 ? clamped : null
|
|
||||||
}
|
|
||||||
|
|
||||||
type DraftAgentParams = {
|
type DraftAgentParams = {
|
||||||
serverId?: string
|
serverId?: string
|
||||||
provider?: string
|
provider?: string
|
||||||
@@ -872,11 +855,9 @@ function DraftAgentScreenContent({
|
|||||||
const modeId = modeOptions.length > 0 && selectedMode !== '' ? selectedMode : undefined
|
const modeId = modeOptions.length > 0 && selectedMode !== '' ? selectedMode : undefined
|
||||||
const trimmedModel = selectedModel.trim()
|
const trimmedModel = selectedModel.trim()
|
||||||
const trimmedThinkingOptionId = selectedThinkingOptionId.trim()
|
const trimmedThinkingOptionId = selectedThinkingOptionId.trim()
|
||||||
const derivedTitle = deriveInitialAgentTitle(text)
|
|
||||||
const config: AgentSessionConfig = {
|
const config: AgentSessionConfig = {
|
||||||
provider: selectedProvider,
|
provider: selectedProvider,
|
||||||
cwd: resolvedWorkingDir,
|
cwd: resolvedWorkingDir,
|
||||||
...(derivedTitle ? { title: derivedTitle } : {}),
|
|
||||||
...(modeId ? { modeId } : {}),
|
...(modeId ? { modeId } : {}),
|
||||||
...(trimmedModel ? { model: trimmedModel } : {}),
|
...(trimmedModel ? { model: trimmedModel } : {}),
|
||||||
...(trimmedThinkingOptionId ? { thinkingOptionId: trimmedThinkingOptionId } : {}),
|
...(trimmedThinkingOptionId ? { thinkingOptionId: trimmedThinkingOptionId } : {}),
|
||||||
|
|||||||
@@ -753,6 +753,93 @@ describe("ClaudeAgentSession redesign invariants", () => {
|
|||||||
await session.close();
|
await session.close();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test("completes a foreground run when only system metadata arrives before the first assistant message", async () => {
|
||||||
|
let step = 0;
|
||||||
|
sdkMocks.query.mockImplementation(() =>
|
||||||
|
createBaseQueryMock(
|
||||||
|
vi.fn(async () => {
|
||||||
|
if (step === 0) {
|
||||||
|
step += 1;
|
||||||
|
return {
|
||||||
|
done: false,
|
||||||
|
value: {
|
||||||
|
type: "system",
|
||||||
|
subtype: "init",
|
||||||
|
session_id: "redesign-metadata-only-session",
|
||||||
|
permissionMode: "default",
|
||||||
|
model: "opus",
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
if (step === 1) {
|
||||||
|
step += 1;
|
||||||
|
return {
|
||||||
|
done: false,
|
||||||
|
value: {
|
||||||
|
type: "system",
|
||||||
|
subtype: "hook_response",
|
||||||
|
session_id: "redesign-metadata-only-session",
|
||||||
|
hook_name: "SessionStart:Callback",
|
||||||
|
hook_event: "SessionStart",
|
||||||
|
stdout: "",
|
||||||
|
stderr: "",
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
if (step === 2) {
|
||||||
|
step += 1;
|
||||||
|
return {
|
||||||
|
done: false,
|
||||||
|
value: {
|
||||||
|
type: "assistant",
|
||||||
|
message: { content: "assistant output" },
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
if (step === 3) {
|
||||||
|
step += 1;
|
||||||
|
return {
|
||||||
|
done: false,
|
||||||
|
value: {
|
||||||
|
type: "result",
|
||||||
|
subtype: "success",
|
||||||
|
usage: buildUsage(),
|
||||||
|
total_cost_usd: 0,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return { done: true, value: undefined };
|
||||||
|
})
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
const session = await createSession();
|
||||||
|
try {
|
||||||
|
const events = await Promise.race([
|
||||||
|
collectUntilTerminal(session.stream("metadata helper prompt")),
|
||||||
|
new Promise<never>((_, reject) => {
|
||||||
|
setTimeout(
|
||||||
|
() => reject(new Error("Timed out waiting for foreground terminal event")),
|
||||||
|
1_000
|
||||||
|
);
|
||||||
|
}),
|
||||||
|
]);
|
||||||
|
|
||||||
|
expect(events.some((event) => event.type === "turn_completed")).toBe(true);
|
||||||
|
|
||||||
|
const assistantText = events
|
||||||
|
.filter(
|
||||||
|
(event): event is Extract<AgentStreamEvent, { type: "timeline" }> =>
|
||||||
|
event.type === "timeline" && event.item.type === "assistant_message"
|
||||||
|
)
|
||||||
|
.map((event) => event.item.text)
|
||||||
|
.join("");
|
||||||
|
expect(assistantText).toContain("assistant output");
|
||||||
|
} finally {
|
||||||
|
await session.close();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
test("reuses one autonomous run for unbound stream_event bursts with no foreground run", async () => {
|
test("reuses one autonomous run for unbound stream_event bursts with no foreground run", async () => {
|
||||||
const session = await createSession();
|
const session = await createSession();
|
||||||
const internal = session as unknown as {
|
const internal = session as unknown as {
|
||||||
|
|||||||
@@ -2746,7 +2746,10 @@ class ClaudeAgentSession implements AgentSession {
|
|||||||
if (!foregroundRun || foregroundRun.promptReplaySeen) {
|
if (!foregroundRun || foregroundRun.promptReplaySeen) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (message.type === "system" && message.subtype === "init") {
|
// System metadata (init/hook callbacks/etc.) can precede the first prompt
|
||||||
|
// replay for a legitimate foreground run. Treating that as churn strands
|
||||||
|
// one-shot helper runs in autonomous fallback.
|
||||||
|
if (message.type === "system") {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this.preReplayMetadataSeen = true;
|
this.preReplayMetadataSeen = true;
|
||||||
|
|||||||
Reference in New Issue
Block a user