From 3b37a217a8e1da4af378c0966e7c5d4c9e44acd9 Mon Sep 17 00:00:00 2001 From: Mikhail Date: Sat, 16 May 2026 20:14:38 +0300 Subject: [PATCH] Use real IDE authoring session --- .../src/components/editor/ide-workspace.tsx | 39 +++++++------- frontend/sfera-web/src/lib/api.ts | 51 +++++++++++++++++-- 2 files changed, 67 insertions(+), 23 deletions(-) diff --git a/frontend/sfera-web/src/components/editor/ide-workspace.tsx b/frontend/sfera-web/src/components/editor/ide-workspace.tsx index c09b2a6..a77c8b3 100644 --- a/frontend/sfera-web/src/components/editor/ide-workspace.tsx +++ b/frontend/sfera-web/src/components/editor/ide-workspace.tsx @@ -39,6 +39,7 @@ import { Badge } from "@/components/ui/badge"; import { Card } from "@/components/ui/card"; import { applyAuthoringChangeSet, + ensureAuthoringSession, getAuthoringSemanticDiffPreview, applyAuthoringMetadataObject, applyAuthoringRollback, @@ -2048,8 +2049,7 @@ function EditorPanel({ routine_name: data.editorSelectedRoutine ?? data.authoringPreview?.context.routine?.name ?? null, original_text: data.editorSourceText, proposed_text: nextProposedText, - task_id: "task.preview", - session_id: "session.preview" + ...(await ensureAuthoringSession(data.projectId, data.apiUrl)) }, data.apiUrl ); @@ -2175,6 +2175,7 @@ function EditorPanel({ const apiUrl = data.apiUrl ?? (typeof window === "undefined" ? resolveApiUrl() : resolveApiUrl(window.location.host)); + const authoringSession = await ensureAuthoringSession(data.projectId, apiUrl); const response = await applyAuthoringChangeSet( data.projectId, { @@ -2182,12 +2183,12 @@ function EditorPanel({ routine_name: data.editorSelectedRoutine ?? data.authoringPreview?.context.routine?.name ?? null, original_text: data.editorSourceText ?? sourceText, proposed_text: proposedText ?? sourceText, - task_id: null, - session_id: "session.ide", - user_id: "current-user", + task_id: authoringSession.task_id, + session_id: authoringSession.session_id, + user_id: authoringSession.user_id, estimated_tokens: 2550, expected_next_version_id: versionPreview.next_version_id, - approved_by: "current-user", + approved_by: authoringSession.user_id, approval_note: t.guardedApplyNote, apply_to_production: false }, @@ -2200,7 +2201,7 @@ function EditorPanel({ status: response.status, target: response.preview.target, version_id: response.version.version_id, - approved_by: "current-user", + approved_by: authoringSession.user_id, approval_note: t.guardedApplyNote, task_id: response.version.task_id, session_id: response.version.session_id, @@ -3261,10 +3262,7 @@ function MetadataDesignerPanel({ .map((command) => ({ name: command.name.trim(), handler: command.handler.trim() || null - })), - task_id: null, - session_id: "session.ide.metadata", - user_id: "current-user" + })) }; const [draftState, setDraftState] = useState<"idle" | "previewing" | "applying" | "applied" | "error">("idle"); const [draftMessage, setDraftMessage] = useState(""); @@ -3311,15 +3309,17 @@ function MetadataDesignerPanel({ const apiUrl = data.apiUrl ?? (typeof window === "undefined" ? resolveApiUrl() : resolveApiUrl(window.location.host)); - const preview = await getAuthoringMetadataObjectPreview(data.projectId, metadataDraft, apiUrl); + const authoringSession = await ensureAuthoringSession(data.projectId, apiUrl); + const sessionDraft = { ...metadataDraft, ...authoringSession }; + const preview = await getAuthoringMetadataObjectPreview(data.projectId, sessionDraft, apiUrl); setDraftDiff(preview.semantic_diff); setDraftState("applying"); const response = await applyAuthoringMetadataObject( data.projectId, { - ...metadataDraft, + ...sessionDraft, expected_next_version_id: preview.version_preview.next_version_id, - approved_by: "current-user", + approved_by: authoringSession.user_id, approval_note: t.metadataDraftDescription, apply_to_production: false }, @@ -3332,7 +3332,7 @@ function MetadataDesignerPanel({ status: response.status, target: response.preview.target, version_id: response.version.version_id, - approved_by: "current-user", + approved_by: authoringSession.user_id, approval_note: t.metadataDraftDescription, task_id: response.version.task_id, session_id: response.version.session_id, @@ -4469,15 +4469,16 @@ function HistoryPanel({ setRollbackState("applying"); setRollbackMessage(""); try { + const authoringSession = await ensureAuthoringSession(data.projectId, data.apiUrl); const response = await applyAuthoringRollback( data.projectId, rollbackPreview.change_id, { expected_rollback_version_id: rollbackPreview.rollback_version_id, - approved_by: "current-user", + approved_by: authoringSession.user_id, approval_note: t.rollbackApplyNote, - task_id: null, - session_id: "session.ide.rollback", + task_id: authoringSession.task_id, + session_id: authoringSession.session_id, apply_to_production: false }, data.apiUrl @@ -4489,7 +4490,7 @@ function HistoryPanel({ status: response.status, target: response.preview.target, version_id: response.version.version_id, - approved_by: "current-user", + approved_by: authoringSession.user_id, approval_note: t.rollbackApplyNote, task_id: response.version.task_id, session_id: response.version.session_id, diff --git a/frontend/sfera-web/src/lib/api.ts b/frontend/sfera-web/src/lib/api.ts index 51d395e..ddd010e 100644 --- a/frontend/sfera-web/src/lib/api.ts +++ b/frontend/sfera-web/src/lib/api.ts @@ -389,6 +389,7 @@ export type AuthoringSemanticDiffPreviewRequest = { source_path?: string | null; task_id?: string | null; session_id?: string | null; + user_id?: string | null; }; export type AuthoringSemanticDiffPreview = { @@ -581,6 +582,12 @@ export type AuthoringChangeSummary = { production_applied: boolean; }; +export type AuthoringSessionContext = { + user_id: string; + task_id: string; + session_id: string; +}; + export function resolveApiUrl(hostHeader?: string | null) { const configuredUrl = process.env.SFERA_API_URL ?? process.env.NEXT_PUBLIC_SFERA_API_URL; if (configuredUrl) { @@ -686,6 +693,39 @@ async function postOptionalJson(apiUrl: string, path: string, body: unknown, } } +function ideAuthoringSessionContext(projectId: string): AuthoringSessionContext { + const safeProjectId = projectId.replace(/[^A-Za-z0-9_.-]/g, "-"); + return { + user_id: "ide.developer", + task_id: `task.ide.${safeProjectId}`, + session_id: `session.ide.${safeProjectId}` + }; +} + +export async function ensureAuthoringSession(projectId: string, apiUrl = resolveApiUrl()): Promise { + const context = ideAuthoringSessionContext(projectId); + await postJson(apiUrl, "/collaboration/users", { + user_id: context.user_id, + display_name: "SFERA IDE" + }); + await postJson(apiUrl, `/security/users/${encodeURIComponent(context.user_id)}/roles/developer`, {}); + await postJson(apiUrl, "/collaboration/tasks", { + task_id: context.task_id, + project_id: projectId, + title: "SFERA IDE authoring", + status: "IN_PROGRESS", + assignee_user_id: context.user_id + }); + await postJson(apiUrl, "/collaboration/sessions", { + session: { + session_id: context.session_id, + task_id: context.task_id, + user_id: context.user_id + } + }); + return context; +} + export async function getDashboardData(apiUrl = resolveApiUrl()) { const [health, summary, snapshots, neo4j, aiUsage, aiPolicy] = await Promise.all([ getJson(apiUrl, "/health"), @@ -874,6 +914,9 @@ export async function getProjectWorkspaceData(projectId: string, apiUrl = resolv : ""; const editorSelectedObject = selectedObjectName ?? snapshotModule?.qualified_name ?? null; const editorSelectedRoutine = selectedModuleRoutine ?? selectedRoutineName ?? null; + const authoringSession = selectedObjectName && authoringSourceText + ? await ensureAuthoringSession(projectId, apiUrl).catch(() => null) + : null; const authoringPreview = needsObjectPanels && selectedObjectName && authoringSourceText ? await postOptionalJson( apiUrl, @@ -883,8 +926,7 @@ export async function getProjectWorkspaceData(projectId: string, apiUrl = resolv routine_name: selectedModuleRoutine, source_path: selectedObjectModule?.source_path ?? null, source_text: authoringSourceText, - task_id: "task.preview", - session_id: "session.preview" + user_id: authoringSession?.user_id ?? null }, null ) @@ -900,8 +942,9 @@ export async function getProjectWorkspaceData(projectId: string, apiUrl = resolv original_text: authoringSourceText, proposed_text: authoringProposedText, source_path: selectedObjectModule?.source_path ?? null, - task_id: "task.preview", - session_id: "session.preview" + task_id: authoringSession?.task_id ?? null, + session_id: authoringSession?.session_id ?? null, + user_id: authoringSession?.user_id ?? null }, null )