mirror of
https://github.com/multica-ai/multica.git
synced 2026-08-04 17:18:35 +02:00
* feat: add QwenPaw ACP backend support Add qwenpaw as a supported agent backend. QwenPaw runs via `qwenpaw acp` over stdio using the ACP (Agent Client Protocol) JSON-RPC 2.0, reusing the hermesClient transport layer. Changes: - New backend: pkg/agent/qwenpaw.go (ACP stdio via hermesClient) - Register in agent.SupportedTypes, New(), launchHeaders - Daemon config: probe qwenpaw binary, QWENPAW_ARGS env var - Display name 'QwenPaw', inline system prompt support - Runtime config: AGENTS.md injection, .qwenpaw/skills/ discovery - User-level skills via QWENPAW_HOME env var - DB migration 224: add qwenpaw to protocol_family whitelist * fix(ci): add qwenpaw to CLI guard names and rename migration 231 - Add 'qwenpaw' to scripts/agent-cli-command-names.txt (TestAgentCLIGuardCoversDefaultCommands) - Rename migration to 231 to avoid collision with existing migrations (TestMigrationNumericPrefixesStayUniqueAfterLegacySet) * fix: address Bohan-J review comments on qwenpaw backend (PR #5986) Blocking fixes: 1. config.go: Add qwenpaw CLI probe so daemon discovers the binary 2. models.go: Add qwenpaw case returning empty list (same as qwen) 3. qwenpaw.go: Fix resume path — use session/load (QwenPaw implements load_session, not session/resume), use resolveResumedSessionID, only set resumeRejected on session-not-found errors, fail the run on set_model failure instead of silently continuing 4. daemon.go: Add qwenpaw→"QwenPaw" to runtimeDisplayNameOverrides 5. agent.go: Update error message to list qwenpaw Non-blocking fixes: 6. qwenpaw_test.go: Add 10 tests covering session/new, session/load, session/load not-found, set_model failure, ListModels, blocked args, protocol verification (session/load not session/resume), timeout, usage tracking, and backend construction 7. sidecar_manifest_test.go: Add qwenpaw to allFileBasedProviders * fix: address Bohan-J second-round review on qwenpaw backend (PR #5986) Blocking fixes: 1. qwenpaw.go: Send qwenpaw.coding_project_dir inside _meta, not as top-level parameter — ACP Pydantic model ignores extra root-level keys (model_config has no extra='allow'), only merges field_meta into kwargs 2. qwenpaw.go: Handle set_model returning result:null (not RPC error) — real QwenPaw server swallows exceptions and returns None, which serialises as JSON null; json.RawMessage('null') is non-nil so bytes.Equal check is needed 3. context.go: Workspace skills are at <workDir>/skills, not <workDir>/.qwenpaw/skills (confirmed against QwenPaw v2.0.1) 4. local_skills.go: QwenPaw resolves global root from QWENPAW_WORKING_DIR -> COPAW_WORKING_DIR -> ~/.copaw -> ~/.qwenpaw, not from QWENPAW_HOME Test additions: 5. TestQwenpawSetModelReturnsNull — regression test for result:null 6. TestQwenpawSessionLoadTransientError — transient error does not set ResumeRejected=true 7. TestQwenpawSessionNewSendsCodingProjectDir — verifies _meta format 8. TestQwenpawSessionLoadSendsCodingProjectDir — same for session/load 9. TestQwenpawTimeout — deterministic sync via signal file All 15 Qwenpaw tests pass. Verified against real qwenpaw v2.0.1 binary (end-to-end: prompt, session ID, resume, system prompt). * fix: rebase against upstream/main and add per-task qwenpaw workspace/agent isolation - Rebase add-qwenpaw-backend branch on upstream/main (88 commits ahead) - Resolve conflict in config.go: keep refactored probe() with shell resolution fallback, which already includes qwenpaw probe - Add --workspace and --agent CLI args to qwenpaw acp for per-task skill isolation and agent identity isolation - Mark --workspace and --agent as blocked in qwenpawBlockedArgs so user custom_args cannot override them - Add deriveQwenpawAgentID() to produce deterministic agent IDs from task's issue ID and agent ID - Wire QwenpawWorkspace and QwenpawAgentID through ExecOptions - Add prepareQwenpawWorkspace() in execenv to materialize bound skills into a per-task workspace directory * fix: address Bohan-J third-round review on qwenpaw backend (PR #5986) Three blocking issues resolved: 1. Agent ID registration — remove session/set_model and --agent entirely. The simpler route: QwenPaw model override is declared unsupported, eliminating the need for agent profile registration in QwenPaw config. 2. Skill revocation — prepareQwenpawWorkspace now does os.RemoveAll on the skill_pool dir and manifest before rebuilding, making it idempotent. A->empty (revoke all), A->B (replace), and A->A (repeated reuse) all work correctly. Added 3 new unit tests. 3. Skill root path — changed 'skills' to 'skill_pool' in both prepareQwenpawWorkspace and skillsDirPath to match QwenPaw's store.py get_workspace_skills_dir. Also cleaned up: removed deriveQwenpawAgentID function and its test, removed QwenpawAgentID from ExecOptions, removed --agent from qwenpawBlockedArgs, removed session/set_model test cases. * fix: add missing qwenpaw probe() call in agents_probe.go The TestDefaultAgentCommandNamesCoversAllProbes test found only 17 probe() calls in agents_probe.go but defaultAgentCommandNames has 18 entries. The probe() call for qwenpaw was missing, causing the backend CI test failure. Adding the probe ensures GUI-launched daemons can resolve qwenpaw via the login shell fallback, matching all other providers. * fix: sync models.go with upstream/main (remove qwenpaw from ListModels) * fix: CI failures — migrate prefix 235→236 + update test - migration prefix 235 was reused by upstream 235_chat_message_quick_actions; renamed our qwenpaw migration from 235 to 236 - TestQwenpawListModels called len() on Catalog struct (compile error); fixed to expect error for unknown provider type * fix: bump qwenpaw migration prefix 236->241 (upstream took 236) * fix: qwenpaw local skill root — 'skills' → 'skill_pool' (MUL-5355) Bohan-J third-round review blocker #3: local_skills.go still scanned <QWENPAW_HOME>/skills but the QwenPaw shared skill pool is <QWENPAW_HOME>/skill_pool (store.py get_workspace_skills_dir). Verified no other qwenpaw paths in the codebase assume the wrong layout. * fix: bump qwenpaw migration prefix 241->242 (upstream took 241) lint test fails with: migration prefix 241 is reused by [241_comment_parent_lookup_index 241_runtime_profile_add_qwenpaw]. Upstream added 241_comment_parent_lookup_index; bump our migration. * feat: add QwenPaw integration test and version declaration (MUL-5355) - New: server/pkg/agent/qwenpaw_integration_test.go with three agentintegration build-tagged tests: TestQwenpawRealACPSmoke — full end-to-end ACP smoke test with session/new → session/prompt and session/load resume validation. TestQwenpawRealWorkspaceSmoke — validates skill_pool workspace flag handling and per-task skill isolation. validateQwenpawVersion — attempt version detection via qwenpaw --version, pip show qwenpaw, and python import. - Document QwenPaw v2.0.1 as the supported baseline version in qwenpaw.go package comment, noting the contract details: _meta qwenpaw.coding_project_dir for Coding Mode, session/set_model NOT supported, skill_pool workspace layout. - This test suite is gated by MULTICA_RUN_REAL_AGENT_SMOKE=1 and requires qwenpaw on PATH, matching the pattern used by grok, cursor, and traeecli integration tests. * fix: bump qwenpaw migration prefix 242->243 (upstream took 242 for qoderclicn) Upstream added 242_runtime_profile_add_qoderclicn in the same rebase window, colliding with our 242_runtime_profile_add_qwenpaw. The migration lint test TestMigrationNumericPrefixesStayUniqueAfterLegacySet catches duplicate prefixes after the legacy range. Also add 'qoderclicn' to our migration's CHECK constraint so it doesn't regress the whitelist added by upstream's 242. * temp: stub out integration test to isolate CI failure * fix: restore upstream probeAgentCLIs() call in config.go (rebase regression) Rebase conflict resolution accidentally reverted upstream's MUL-5439 refactor (extracting probe logic to agents_probe.go) back to the old inline probe block. This also dropped qoderclicn detection and duplicated the probe logic already in agents_probe.go. Restore the single 'agents := probeAgentCLIs()' call — qwenpaw is already probed in agents_probe.go. * fix: bump qwenpaw migration prefix 243->251 (upstream took 243-250) Upstream added migrations 243-250 since our last rebase. Bump to 251, the next available prefix. * feat: add QwenPaw integration test (agentintegration build tag) TestQwenpawRealACPSmoke drives the real qwenpaw acp binary end-to-end: - session/new + session/prompt produces 'pong' - session/load resume with ResumeSessionID works - --workspace flag is forwarded correctly Gated by MULTICA_RUN_REAL_AGENT_SMOKE=1, matching grok/cursor pattern. Validated against QwenPaw v2.0.1. * fix: workspace skills dir 'skills' not 'skill_pool' + add skill loading integration test Two fixes: 1. qwenpaw_workspace.go: write skills to <workspace>/skills/ instead of <workspace>/skill_pool/. QwenPaw's get_workspace_skills_dir() looks for workspace skills at <workspace>/skills/ (store.py:65-67), not skill_pool (which is the shared pool at WORKING_DIR/skill_pool). Verified against real qwenpaw acp — skills in skill_pool/ were never discovered. 2. Add TestQwenpawRealWorkspaceSkill integration test that proves a bound skill is actually loaded and effective: writes a skill that overrides the agent's response, sends an unrelated prompt, and asserts the skill's marker text appears in the output. This addresses R3 review feedback: 'please also add a test that exercises an actually-bound skill'. All three integration tests pass against QwenPaw v2.0.1: - TestQwenpawRealACPSmoke (session/new + prompt + session/load resume) - TestQwenpawRealWorkspaceSkill (skill discovery + effectiveness) * feat: add ACP model discovery for qwenpaw via session/new models field QwenPaw v2.0.1+ now includes a 'models' field (SessionModelState) in the session/new response, added by agentscope-ai/QwenPaw#6531. This lets ACP clients discover available models without session/set_model. - ListModels for qwenpaw now uses discoverACPModels (same pattern as traecli/grok/kiro) to spin up 'qwenpaw acp', call session/new, and parse the models catalog from the response. - discoverQwenpawModels mirrors discoverTraecliModels — ACP-native, no auth selection needed. - Model override via session/set_model remains unsupported: it persists to agent.json at the agent scope (not session-scoped), so calling it would mutate the user's shared agent config. The model picker shows available models for display/selection, but the daemon does not send set_model. - Updated TestQwenpawListModels to verify qwenpaw is a recognized type (not 'unknown agent type' error). * fix: address Bohan-J Review 5 — ModelSelectionSupported=false, version bump to v2.1.0-beta.1, remove debug files - ModelSelectionSupported('qwenpaw') now returns false with rationale (session/set_model persists to agent scope, not session scope) - Add TestQwenpawModelSelectionUnsupported regression test - Update version references from v2.0.1 to v2.1.0-beta.1 (includes agentscope-ai/QwenPaw#6531 — models field in session/new response) - Remove check_ci.py, jobs.json, runs.json debug artifacts * fix: bump qwenpaw migration prefix 251->253 (upstream took 251) Upstream added 251_agent_runtime_unbind. Bump to 253, the next available prefix after 252_agent_builder_draft. * fix: address Bohan-J Review 6 — drop unused discovery, always attribute to unknown - ListModels for qwenpaw returns empty catalog without spawning ACP subprocess (model selection is unsupported, so no consumer exists) - Usage attribution always uses 'unknown' instead of opts.Model (the backend never sends opts.Model to QwenPaw) - Add TestQwenpawUsageModelIgnored regression test - Fix stale v2.0.1 comment in TestQwenpawListModels * chore(agent): clean up qwenpaw model-discovery leftovers Follow-up nits from review 7 on PR #5986: - Drop discoverQwenpawModels: it lost its only caller when ListModels started returning an empty catalog for qwenpaw. - Correct the version contract in qwenpaw.go. The execution path needs only the ACP surface present in v2.0.1 (current stable); the models field on session/new landed in v2.1.0-beta.1 but has no consumer now that model selection is unsupported. - Make TestQwenpawListModels actually guard the no-subprocess promise. It pointed at a nonexistent path, which the old discovery helper also answered with an empty catalog, so it passed either way. It now uses an executable fake that records invocation; verified it fails if a discovery path is reintroduced. - gofmt agent.go (ExecOptions alignment broke when QwenpawWorkspace was added) and restore the trailing newline in qwenpaw_test.go. Co-authored-by: multica-agent <github@multica.ai> --------- Co-authored-by: niudakok <niudakok@users.noreply.github.com> Co-authored-by: Bohan-J <bohan.optimism@gmail.com> Co-authored-by: multica-agent <github@multica.ai>