fix(ai): sync auto-commit with streamed result to surface commit SHA to UI (Fix 10)
This commit is contained in:
@@ -872,55 +872,70 @@ export async function POST(request: Request) {
|
|||||||
// view — without it, every fs.write / shell.exec mutation
|
// view — without it, every fs.write / shell.exec mutation
|
||||||
// stays trapped in the dev container's volume.
|
// stays trapped in the dev container's volume.
|
||||||
//
|
//
|
||||||
// Run AFTER the assistant message is persisted because the
|
// Run BEFORE the final done event so we can surface the commit
|
||||||
// user already saw the reply; a slow push shouldn't block
|
// result in the UI (Fix 10).
|
||||||
// the chat. If there's nothing to commit, the helper short-
|
|
||||||
// circuits with reason='clean' in <1s.
|
|
||||||
if (
|
if (
|
||||||
activeProject?.id &&
|
activeProject?.id &&
|
||||||
activeProject?.slug &&
|
activeProject?.slug &&
|
||||||
typeof activeProject?.giteaCloneUrl === "string"
|
typeof activeProject?.giteaCloneUrl === "string"
|
||||||
) {
|
) {
|
||||||
(async () => {
|
try {
|
||||||
try {
|
// Best-effort clone in case the pre-loop kick-off was
|
||||||
// Best-effort clone in case the pre-loop kick-off was
|
// racing with container provisioning and never landed.
|
||||||
// racing with container provisioning and never landed.
|
await ensureProjectRepoCloned({
|
||||||
await ensureProjectRepoCloned({
|
projectId: activeProject.id,
|
||||||
projectId: activeProject.id,
|
projectSlug: activeProject.slug,
|
||||||
projectSlug: activeProject.slug,
|
giteaCloneUrl: activeProject.giteaCloneUrl,
|
||||||
giteaCloneUrl: activeProject.giteaCloneUrl,
|
}).catch(() => null);
|
||||||
}).catch(() => null);
|
// Commit message: prefer the assistant's own first
|
||||||
// Commit message: prefer the assistant's own first
|
// sentence (one line, ≤200 chars). Falls back to a
|
||||||
// sentence (one line, ≤200 chars). Falls back to a
|
// generic checkpoint when the assistant only made
|
||||||
// generic checkpoint when the assistant only made
|
// tool calls without prose.
|
||||||
// tool calls without prose.
|
const firstSentence = (assistantText || "")
|
||||||
const firstSentence = (assistantText || "")
|
.split(/(?<=[.!?])\s+/)[0]
|
||||||
.split(/(?<=[.!?])\s+/)[0]
|
?.trim()
|
||||||
?.trim()
|
?.slice(0, 180);
|
||||||
?.slice(0, 180);
|
const commitMessage = firstSentence || "AI checkpoint";
|
||||||
const message = firstSentence || "AI checkpoint";
|
|
||||||
const result = await commitAndPushIfDirty({
|
const commitPromise = commitAndPushIfDirty({
|
||||||
projectId: activeProject.id,
|
projectId: activeProject.id,
|
||||||
projectSlug: activeProject.slug,
|
projectSlug: activeProject.slug,
|
||||||
message,
|
message: commitMessage,
|
||||||
});
|
});
|
||||||
if (result.committed) {
|
const timeoutPromise = new Promise<{
|
||||||
console.log(
|
committed: false;
|
||||||
`[chat] auto-commit project=${activeProject.slug} sha=${result.sha} pushed=${result.pushed}`,
|
reason: string;
|
||||||
);
|
}>((resolve) =>
|
||||||
} else if (
|
setTimeout(
|
||||||
result.reason &&
|
() => resolve({ committed: false, reason: "timeout" }),
|
||||||
result.reason !== "clean" &&
|
8000,
|
||||||
result.reason !== "no_repo"
|
),
|
||||||
) {
|
);
|
||||||
console.warn(
|
|
||||||
`[chat] auto-commit failed project=${activeProject.slug} reason=${result.reason}`,
|
const result = (await Promise.race([
|
||||||
);
|
commitPromise,
|
||||||
}
|
timeoutPromise,
|
||||||
} catch (err) {
|
])) as any;
|
||||||
console.warn("[chat] auto-commit fire-and-forget failed", err);
|
|
||||||
|
if (result.committed) {
|
||||||
|
emit({ type: "commit", sha: result.sha, pushed: result.pushed });
|
||||||
|
console.log(
|
||||||
|
`[chat] auto-commit project=${activeProject.slug} sha=${result.sha} pushed=${result.pushed}`,
|
||||||
|
);
|
||||||
|
} else if (
|
||||||
|
result.reason &&
|
||||||
|
result.reason !== "clean" &&
|
||||||
|
result.reason !== "no_repo"
|
||||||
|
) {
|
||||||
|
emit({ type: "commit_failed", reason: result.reason });
|
||||||
|
console.warn(
|
||||||
|
`[chat] auto-commit failed project=${activeProject.slug} reason=${result.reason}`,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
})();
|
} catch (err) {
|
||||||
|
emit({ type: "commit_failed", reason: String(err) });
|
||||||
|
console.warn("[chat] auto-commit failed", err);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fire-and-forget: ask Gemini for a 1-2 sentence "what got done"
|
// Fire-and-forget: ask Gemini for a 1-2 sentence "what got done"
|
||||||
|
|||||||
Reference in New Issue
Block a user