From 5f939ce601396b685351ac22debea57bd10f957f Mon Sep 17 00:00:00 2001 From: lefarcen <935902669@qq.com> Date: Fri, 22 May 2026 17:53:27 +0800 Subject: [PATCH] fix(web): remove Ingest source panel from Automations tab (#2711) * fix(web): remove Ingest source panel from Automations tab The Automations tab carried a free-form "Ingest source" composer that let users paste arbitrary content (URL, repo path, connector event, chat snippet) and turn it into a source packet plus evolution proposals. The form was confusing next to the routine/template flow on the same page, exposed an internal canonicalization concept users don't need to think about, and shipped before the surrounding evolution-proposal flow was wired into a coherent end-to-end story. Drop the UI surface only: - Remove the
block, the Template / Source / Compression / Connector selects, the title/source ref/content fields, the recent-packets list, and the Ingest button. - Drop the now-dead local state (sourcePackets / sourceForm / ingestingSource), the patchSourceForm and submitSourceIngestion helpers, the SOURCE_KIND_OPTIONS / COMPRESSION_OPTIONS constants, the SourceIngestionForm type and DEFAULT_SOURCE_FORM, the /api/automation-source-packets refresh leg, and the sourcePackets side-write inside crystallizeRun. - Remove the matching .automations-ingest / .automation-ingest-* CSS block (plus the two responsive overrides) from tasks.css. - Delete the test case that drove the form in TasksView.templates.test. Backend stays intact: apps/daemon/src/automation-ingestions.ts, the POST /api/automation-ingestions route, `od automation ingest` CLI, the routine-evolution call site, and the AutomationContentPacket / AutomationSourceKind / AutomationTokenCompressionMode contracts all remain, since routine scheduling still depends on them. * fix(web): drop crystallize test assertion on removed packet list The crystallize test was asserting that the new content packet's title shows up on the page. That assertion only passed because the daemon response was being side-written into the deleted sourcePackets state and rendered in the Ingest source recent-packets strip. With that UI removed, the packet title has no surface to land on; the proposal title (`Skill: Artifact polish loop run`) is still asserted and remains the real signal that crystallize succeeded. --- apps/web/src/components/TasksView.tsx | 244 +----------------- apps/web/src/styles/home/tasks.css | 143 ---------- .../components/TasksView.templates.test.tsx | 108 -------- 3 files changed, 1 insertion(+), 494 deletions(-) diff --git a/apps/web/src/components/TasksView.tsx b/apps/web/src/components/TasksView.tsx index a88673e2a1..3ed87f4757 100644 --- a/apps/web/src/components/TasksView.tsx +++ b/apps/web/src/components/TasksView.tsx @@ -4,15 +4,10 @@ import { useCallback, useEffect, useMemo, useState } from 'react'; import type { - AutomationContentPacket, AutomationEvolutionProposal, AutomationEvolutionProposalListResponse, - AutomationSourceIngestionResponse, - AutomationSourceKind, - AutomationSourcePacketListResponse, AutomationTemplate as ContractAutomationTemplate, AutomationTemplateListResponse, - AutomationTokenCompressionMode, ConnectorDetail, Routine, RoutineRun, @@ -162,41 +157,6 @@ const TEMPLATE_FILTERS: ReadonlyArray<{ id: TemplateFilter; label: string }> = [ { id: 'quality', label: 'Quality' }, ]; -const SOURCE_KIND_OPTIONS: ReadonlyArray<{ id: AutomationSourceKind; label: string }> = [ - { id: 'connector', label: 'Connector' }, - { id: 'url', label: 'URL' }, - { id: 'repo', label: 'Repo' }, - { id: 'artifact', label: 'Artifact' }, - { id: 'chat', label: 'Chat' }, - { id: 'upload', label: 'Upload' }, -]; - -const COMPRESSION_OPTIONS: ReadonlyArray<{ id: AutomationTokenCompressionMode; label: string }> = [ - { id: 'balanced', label: 'Balanced' }, - { id: 'aggressive', label: 'Aggressive' }, - { id: 'off', label: 'Off' }, -]; - -type SourceIngestionForm = { - templateId: string; - sourceKind: AutomationSourceKind; - sourceRef: string; - title: string; - bodyMarkdown: string; - connectorId: string; - tokenCompression: AutomationTokenCompressionMode; -}; - -const DEFAULT_SOURCE_FORM: SourceIngestionForm = { - templateId: 'ingest-source-memory-tree', - sourceKind: 'connector', - sourceRef: '', - title: '', - bodyMarkdown: '', - connectorId: '', - tokenCompression: 'balanced', -}; - function scheduleStatusLabel(routine: Routine): string { if (!routine.enabled) return 'Paused'; return describeScheduleSummary(routine.schedule); @@ -405,10 +365,7 @@ export function TasksView({ skills = [], designTemplates = [], connectors = [] } const [templateFilter, setTemplateFilter] = useState('all'); const [automationCatalog, setAutomationCatalog] = useState([]); const [proposals, setProposals] = useState([]); - const [sourcePackets, setSourcePackets] = useState([]); - const [sourceForm, setSourceForm] = useState(DEFAULT_SOURCE_FORM); const [proposalBusyId, setProposalBusyId] = useState(null); - const [ingestingSource, setIngestingSource] = useState(false); const [crystallizingRunId, setCrystallizingRunId] = useState(null); const [expandedId, setExpandedId] = useState(null); const [historyTick, setHistoryTick] = useState(0); @@ -436,18 +393,11 @@ export function TasksView({ skills = [], designTemplates = [], connectors = [] } return (await res.json()) as AutomationEvolutionProposalListResponse; }) .catch(() => null); - const sourcePacketRequest = fetch('/api/automation-source-packets?limit=3') - .then(async (res) => { - if (!res.ok) return null; - return (await res.json()) as AutomationSourcePacketListResponse; - }) - .catch(() => null); - const [rRes, pRes, tJson, proposalJson, sourcePacketJson] = await Promise.all([ + const [rRes, pRes, tJson, proposalJson] = await Promise.all([ fetch('/api/routines'), fetch('/api/projects'), templateRequest, proposalRequest, - sourcePacketRequest, ]); if (!rRes.ok) throw new Error(`routines: ${rRes.status}`); const rJson = await rRes.json(); @@ -467,9 +417,6 @@ export function TasksView({ skills = [], designTemplates = [], connectors = [] } if (proposalJson) { setProposals(Array.isArray(proposalJson.proposals) ? proposalJson.proposals : []); } - if (sourcePacketJson) { - setSourcePackets(Array.isArray(sourcePacketJson.packets) ? sourcePacketJson.packets : []); - } setError(null); } catch (err) { setError(err instanceof Error ? err.message : String(err)); @@ -490,61 +437,6 @@ export function TasksView({ skills = [], designTemplates = [], connectors = [] } const activeCount = routines.filter((routine) => routine.enabled).length; const pausedCount = routines.length - activeCount; - const sourceIngestionTemplates = useMemo( - () => - automationCatalog.filter((template) => - template.stages.some((stage) => stage.kind === 'ingest' || stage.kind === 'propose'), - ), - [automationCatalog], - ); - - const patchSourceForm = (patch: Partial) => { - setSourceForm((current) => ({ ...current, ...patch })); - }; - - const submitSourceIngestion = async () => { - if (!sourceForm.bodyMarkdown.trim()) { - setError('Paste source content before ingesting it.'); - return; - } - setIngestingSource(true); - setError(null); - try { - const res = await fetch('/api/automation-ingestions', { - method: 'POST', - headers: { 'content-type': 'application/json' }, - body: JSON.stringify({ - templateId: sourceForm.templateId || undefined, - sourceKind: sourceForm.sourceKind, - sourceRef: sourceForm.sourceRef || undefined, - title: sourceForm.title || undefined, - bodyMarkdown: sourceForm.bodyMarkdown, - connectorId: - sourceForm.sourceKind === 'connector' && sourceForm.connectorId - ? sourceForm.connectorId - : undefined, - tokenCompression: sourceForm.tokenCompression, - }), - }); - if (!res.ok) { - const j = await res.json().catch(() => ({})); - throw new Error(j.error || `ingestion failed: ${res.status}`); - } - const json = (await res.json()) as AutomationSourceIngestionResponse; - setSourcePackets((current) => [json.packet, ...current].slice(0, 3)); - setSourceForm((current) => ({ - ...current, - title: '', - sourceRef: '', - bodyMarkdown: '', - })); - await refresh(); - } catch (err) { - setError(err instanceof Error ? err.message : String(err)); - } finally { - setIngestingSource(false); - } - }; const reviewProposal = async (id: string, action: 'apply' | 'reject') => { setProposalBusyId(id); @@ -607,8 +499,6 @@ export function TasksView({ skills = [], designTemplates = [], connectors = [] } const j = await res.json().catch(() => ({})); throw new Error(j.error || `crystallize failed: ${res.status}`); } - const json = (await res.json()) as RoutineRunCrystallizeResponse; - setSourcePackets((current) => [json.packet, ...current].slice(0, 3)); await refresh(); } catch (err) { setError(err instanceof Error ? err.message : String(err)); @@ -897,138 +787,6 @@ export function TasksView({ skills = [], designTemplates = [], connectors = [] }
) : null} -
-
-
-

Ingest source

-

- Turn connector, repo, artifact, or chat context into reviewable evolution proposals. -

-
- {sourcePackets.length} recent -
-
-
- - - - {sourceForm.sourceKind === 'connector' ? ( - - ) : null} -
-
- - -
-