Skip to content

Add session Project linking/create flow and explicit local-only embeddings #544

Description

@chsimonpan

Add session project/create linking and explicit local-only embedding configuration

User problem

Users cannot organize an already-running session under a Project unless they also choose an existing Work Item. They also cannot create a Project or Work Item as part of that linking flow. Separately, users who want semantic memory to remain on-device cannot explicitly configure a local OpenAI-compatible embedding endpoint and be sure the app will not fall back to a stored cloud credential.

Current upstream gap

Verified against official/develop at 2c7b1b6fcc8551b56640126500bae0f4257bd6a6 (2026-07-27)

  • Existing-Work-Item linking is already end-to-end: UI picker at src/engines/ChatPanel/panels/LinkSessionToWorkItemModal.tsx, TypeScript RPC in src/api/tauri/agent/session.ts, desktop command agent_link_session_to_work_item in src-tauri/crates/agent-core/src/state/commands/session/persistence.rs, and durable session/work-item updates in that file plus src-tauri/crates/agent-core/src/core/session/persistence/crud/ops.rs.
  • The Work Item picker only lists existing items; it has no create action. There is no project-only link command or Project picker in the upstream command list/RPC/UI.
  • EmbeddingConfig contains only provider and model, defaulting to "auto" (src-tauri/crates/agent-core/src/integrations/config.rs). AutoEmbeddingProvider tries configured local resolution and then scans Azure/OpenAI/OpenRouter/ZenMux/DeepSeek credentials (src-tauri/crates/agent-core/src/specialization/memory/embeddings/auto.rs). Its current local resolver returns “not available,” so a user cannot configure an endpoint/model for local-only use.

Inferred

  • A single “Link session” surface with Project and Work Item targets will be less surprising than two partially overlapping menus.
  • Create-and-link should be transactional from the user’s point of view: on failed link, report the created object and offer retry rather than silently losing it.

Proposed solution

Add a unified “Link session…” action with four explicit choices:

  1. link to an existing Project;
  2. create a Project and link the session to it;
  3. link to an existing Work Item (retain the current flow); and
  4. create a Work Item in a selected/new Project and link the session to it.

Add application-level embedding settings with an explicit local_qwen/local-only mode: endpoint, model, expected dimensions, timeout, max input size, and cadence. In local-only mode, use only the configured local OpenAI-compatible endpoint, send no API key, and never fall back to Key Vault or any remote provider. Keep remote embedding as a separately selected opt-in mode; disabled remains available.

Implementation notes

Verified candidate evidence (not upstream)

  • dba725a036bcfa9ac1f0c301d2645de87b04fe79 (feat(session): link sessions to projects and harden side queries) adds update_project_link, agent_link_session_to_project, the Tauri handler registration, Zod/RPC/client wiring, and LinkSessionToProjectModal.tsx. The persistence test is in src-tauri/crates/agent-core/src/core/session/persistence/crud/ops_tests.rs.
  • 5fb0dc452b836c4754f6559155326304947b7c63 adds the ChatPanel/sidebar entry point and fixes the Project search input.
  • 4e3f63535e326d11f9ed14fa677f55113edbf9b9 adds bounded embedding configuration and explicit provider resolution in src-tauri/crates/agent-core/src/integrations/config.rs and src-tauri/crates/agent-core/src/specialization/memory/embeddings/{auto,openai}.rs: local endpoint/model, dimensions validation, bounded input, and timeouts. Its auto alias resolves only to the local provider, not remote fallback.
  • 6cdb1cad6e4c559c82887ec64d22cc2fb2cd16a8 adds an embedding settings panel and frontend configuration tests. Do not copy its later default of embedding_api for this issue: that conflicts with the explicit local-only/no-fallback goal.

Required design details

  • Add update_project_link(session_id, org_id, project_id, project_name, project_slug). It must set project metadata and clear work_item_id; validate that both session and Project exist, emit DATA_CHANGED_EVENT, and return the refreshed session record.
  • Reuse the existing Work Item command’s replacement cleanup (remove_linked_session_from_work_item) when changing from one Work Item to another or from a Work Item to project-only. A project-only link must not leave a stale linked_sessions entry in the old Work Item.
  • Reuse existing project/work-item persistence APIs for creation; do not manufacture IDs in the modal. Preserve organization scope and validate the selected Project before durable writes.
  • Treat “create then link” as two durable operations with clear recoverability. If creation succeeds but linking fails, show the created Project/Work Item and a retry action.
  • Extend EmbeddingConfig additively and with serde defaults for existing configuration files. For local_qwen, require non-empty endpoint and model, normalize the endpoint, enforce dimensions when configured, bound request input, and use a finite request timeout. Explicit local-only configuration must never consult KEY_SERVICE.
  • Add an explicit UI status (“disabled”, “local-only ready/unreachable”, or “remote configured”) without probing/sending session text during settings render.

Acceptance criteria

  • From an existing session, a user can search and link an existing Project; the session persists org_id, project_id, project_name, and project_slug, and has no work_item_id.
  • A user can create a Project and then link the existing session. A user can create a Work Item under a selected/new Project and link it; the Work Item lists the session exactly once.
  • Existing Work Item linking remains available, removes any previous Work Item’s stale linked-session record, and updates the session and new Work Item consistently.
  • Closing a picker or changing search text cannot apply a stale asynchronous result.
  • local_qwen/local-only embedding configuration uses only its configured local endpoint, model, and no API key. With a reachable endpoint it produces compatible vectors; with an unreachable/malformed endpoint it fails safely and does not try Key Vault/remote providers.
  • Existing configuration files deserialize successfully; disabled and remote modes behave only when explicitly selected.
  • UI text is localized and tests cover the persistence/RPC path, picker create/link paths, local-only no-fallback policy, malformed embedding responses, dimension mismatch, timeout, and bounded input.

Exact validation commands

Run from the official-base worktree (/tmp/org2-official-build-fast-20260727, after the implementation is applied):

pnpm vitest run src/engines/ChatPanel/panels/linkSessionToWorkItemModel.test.ts src/modules/Memory/sessionMemoryEmbeddingConfig.test.ts
cd src-tauri && cargo test -p agent_core project_link_persists_project_metadata_and_clears_work_item
cd src-tauri && cargo test -p agent_core specialization::memory::embeddings::auto::tests::explicit_policy
cd src-tauri && cargo test -p agent_core specialization::memory::embeddings::openai::tests
pnpm typecheck
pnpm tauri:build:fast

The repository’s exact “build fast” command is pnpm tauri:build:fast; package.json maps it to node scripts/tauri/build-fast-parallel.cjs, and scripts/tauri/build-fast-parallel.cjs documents the same invocation.

Base and evidence

  • Most appropriate official base ref: official/develop at 2c7b1b6fcc8551b56640126500bae0f4257bd6a6.
  • No existing worktree is fully clean at the official tip. The most appropriate base worktree is /tmp/org2-official-build-fast-20260727 at that exact commit (branch ash/official-issue-build-fast-20260727); it has only an untracked .ash-reports/ directory at inspection, so use it only after preserving/removing that unrelated directory or create a fresh detached worktree from official/develop.
  • Candidate-only worktrees: /tmp/org2-session-link-hotfix-20260727 at 5fb0dc452… (one unrelated uncommitted file at inspection) and /mnt/panshuainan/org2-embedding-20260720 at 3b083bfd… (heavily dirty). Neither candidate commit chain is an ancestor of official/develop.

Build validation

Validated against upstream official/develop at 2c7b1b6fcc8551b56640126500bae0f4257bd6a6.

The repository-defined command pnpm tauri:build:fast passed in the project Docker build environment (the host lacks GTK development packages):

Built application at: /build-target/dev-build/org2
Bundling ORG2_1.2.2_amd64.deb
[build-fast-parallel] Total: 1050.0s

Artifact: ORG2_1.2.2_amd64.deb (69,455,190 bytes).

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions