#2160

feat: save a finished Task as a Harbor task and submit it to Env Hub

Draftfengju0213wants to merge intofrom

Related Issue

None filed yet. This is Eigent's side of Env Hub, which turns finished agent tasks into Harbor environments. The connector is eigent-ai/open-connector#5 (the env_hub provider), and Env Hub itself is eigent-ai/harbor-hub#158.

Description

A completed Task gets two actions next to Copy and feedback, in both the default event-native timeline (NarrativeTimeline) and the rollback renderer (UserQueryGroup).

Save as Harbor task

  • Writes the Task as a Harbor task folder under Downloads/eigent-harbor-tasks, built only from what Eigent already recorded. Nothing leaves the machine and no model is called.
    • The workspace is the Space at the Content Repository commit before the Task, without build caches, secrets, or binaries the Task never named. Each file left out is listed with its reason.
    • instruction.md is the user's own words; workspace paths become /app.
    • The verifier and solution are placeholders, to be written by Env Hub.
  • Backend: GET /runs/{run_id}/git/harbor-task says whether the Task can be saved; POST saves it (backend/app/harbor_task/exporter.py). The Git backend can list a commit's tree (GitBackend.tree_entries).
  • The saved folder opens through a new IPC channel, reveal-harbor-task, that only allows that folder (electron/main/harborTasks.ts).

Submit to Env Hub

  • Shown only where the Connector Gateway offers Env Hub (env_hub). The check runs once per app session, so cards that can't use it show nothing.
  • If Env Hub isn't connected, a toast offers to open its connector in Settings → Connectors. For this, openSettings('connectors', { connectorTarget }) is new.
  • Otherwise the local backend bundles the saved task with the conversation: the user's messages and answers and the agent's replies, with secrets redacted (POST .../harbor-task/bundle, backend/app/harbor_task/bundle.py). A confirmation then shows what will be uploaded and its size. Nothing is sent until the user confirms.
  • On confirm:
    • env_hub.create_upload returns a one-time upload URL.
    • The local backend PUTs the bytes straight to that URL (.../bundle/upload). It allows https only (http only for localhost), PUT only, and upload headers only, and the bytes never pass through the connector.
    • env_hub.submit_conversion starts the conversion.
  • The button follows the conversion every 20 s while the card is shown, and the last conversion per Task is remembered on this computer. When the environment is ready, the button says so and opens it.
  • executeConnectorAction in src/api/connectors.ts unwraps the gateway's reply and throws its message and code.

Also

  • useRunHarborTaskAvailability is shared by both buttons, with one request per Task in flight.
  • The authStore mock in chatStore.feedbackIdentity.test.tsx can now be called as the hook it stands for.
  • Copy is in all 11 locales: the harbor-task-* keys and 26 Env Hub keys (submit-to-env-hub, env-hub-*).

Also fixed: a Git Run whose Space folder is gone

Found while testing this. When a Space's folder is moved or deleted, finishing a Git Run that ended in it fails, and startup retries every terminal Run, so the same Run fails at every launch. WorkspaceGitLifecycle.finalize_run now checks that the worktree folder exists first. If it is gone, the Run is skipped as workspace_missing with an info log. Nothing is persisted, so the Run is finished at a later startup if the folder comes back (backend/app/workspace_git/lifecycle.py, test test_a_run_whose_space_folder_is_gone_is_skipped_not_failed).

UI

  • Primitives: Button (variant="ghost", size="sm", buttonContent="icon-only"), DsIcon, ConfirmModal (confirmVariant="primary") and sonner toasts.
  • Icons: lucide CloudUpload, CloudCheck, CloudAlert and Loader2.
  • No raw colours or new tokens. check:design-tokens passes.

Testing Evidence

Automated (on current main):

  • New frontend tests: SubmitToEnvHubButton.test.tsx (6) covers these cases:
    • Hidden without the provider.
    • Connect first.
    • Nothing sent before confirming, then upload and convert.
    • Cancel sends nothing.
    • Following a conversion until it's ready, then opening it.
    • A failed submit offers to submit again.
  • Other focused frontend tests: SaveAsHarborTaskButton.test.tsx, the TimelineModes tests, chatStore.feedbackIdentity and the settings tests. Altogether 12 files and 191 tests passed.
  • ChatBox and store suites: test/unit/components/ChatBox and test/unit/store pass with the default env and with VITE_CHATBOX_EVENT_BUS=false. The exception is the same 9 tests that already fail on main: QueuedBox.test.tsx (6) and installationStore.test.ts (3).
  • Backend: tests/app/harbor_task, tests/app/controller/test_workspace_git_controller.py and tests/app/workspace_git (with the Run fix): 290 passed. ruff passes on the changed files.
  • Static checks: npm run type-check, ESLint on the changed files, check:i18n, check:design-tokens and git diff --check pass.

End to end, outside the app: the same flow was run against Env Hub staging through a local open-connector gateway with the env_hub provider. It covered OAuth connect, create_upload, a direct PUT, submit_conversion, get_conversion and get_environment, and the environment passed its gates.

In the app: to be tested by hand. Save as Harbor task works on its own; Submit to Env Hub needs an eigent_server whose Connector Gateway offers env_hub (open-connector#5). Screenshots of both buttons, the confirmation and the toasts in light and dark themes will be added before this leaves draft.

  • I have included human-verified testing evidence in this PR.
  • This PR includes frontend/UI changes, and I attached screenshot(s) or screen recording(s).
  • No frontend/UI changes in this PR.

What is the purpose of this pull request?

  • Bug fix
  • New Feature
  • Documentation update
  • Other

Contribution Guidelines Acknowledgement

🤖 Generated with Claude Code

Activity

3

This pull request is a draft

Merging is unavailable until it is marked ready for review.