mirror of
https://github.com/multica-ai/multica.git
synced 2026-07-24 19:41:14 +02:00
agent/lambda/f128b05c
8 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
ef75f80d9d |
fix(daemon): clean stale agent branches during repo gc (MUL-2550) (#3039)
* fix(daemon): 清理陈旧 agent 分支 Co-authored-by: multica-agent <github@multica.ai> * fix(daemon): 串行化 bare repo gc Co-authored-by: multica-agent <github@multica.ai> * test(daemon): adapt health repo cache mock Co-authored-by: multica-agent <github@multica.ai> * fix(daemon): gate gc maintenance on stale-branch deletion Address review feedback on the bare-repo GC change: - Only run `reflog expire` + `git gc --prune=30.days` when we actually deleted a stale agent branch this cycle. Previously the heavy step ran every GC tick on every cached repo even when there was nothing to reclaim, turning a stale-ref cleanup into a periodic full-repo maintenance job under the per-repo lock. - Split git command timeouts: `gc --prune=30.days` now gets a 10-minute budget instead of sharing the 30s ceiling that was scoped for the original `worktree prune` call. Light commands stay at 30s. - Drop the redundant `gc --auto` — `gc --prune=30.days` already performs the maintenance `gc --auto` would have triggered. - Narrow the agent-namespace ref query from `refs/heads/agent` to `refs/heads/agent/` so the pattern can't surface a literal `agent` branch outside the daemon namespace. Tests: - New TestPruneWorktree_IgnoresLiteralAgentBranch pins the trailing- slash narrowing. - New TestPruneWorktree_SkipsMaintenanceWhenNothingDeleted uses an unreachable, backdated loose object as a sentinel to verify that `gc --prune` runs only when a stale agent branch was reaped. Co-authored-by: multica-agent <github@multica.ai> --------- Co-authored-by: 0xNini Code Dev <agent@multica.local> Co-authored-by: multica-agent <github@multica.ai> Co-authored-by: 0xNini <0xnini@iMac-Pro.local> Co-authored-by: J <j@multica.ai> |
||
|
|
ccbd62c7ad |
fix(daemon): ignore gc meta with empty parent ids (#3407)
Co-authored-by: “646826” <“646826@gmail.com”> |
||
|
|
341ce7bfa5 |
feat: support local working directory for projects (MUL-2618 v1) (#3283)
* feat(project): add local_directory project_resource type (MUL-2662)
Adds a second project_resource type alongside github_repo so a project
can be pinned to an existing directory on a specific daemon (the v1 of
the local-working-directory flow tracked in MUL-2618). The ref schema is
{ local_path, daemon_id, label? }; local_path must be absolute and
daemon_id is required. The same (daemon_id, local_path) pair is allowed
on multiple projects by design — no UNIQUE constraint is added.
Implementation reuses the existing project_resource API surface: the new
type is wired through the validator switch with no migration, no new
events, and no daemon-handler changes (daemon already passes through
arbitrary resource types via ProjectResources). The CLI gains
--local-path / --daemon-id / --ref-label shortcuts so
`multica project resource add --type local_directory` mirrors the
existing `--type github_repo --url ...` ergonomics; the generic --ref
flag still works for both types.
Tests cover the full CRUD lifecycle, the same-path-across-projects
allowance, the same-path-same-project conflict, the validator rejections
(missing/blank/relative path, missing daemon_id, wrong payload type),
and the cross-platform isAbsoluteLocalPath helper.
Co-authored-by: multica-agent <github@multica.ai>
* feat(project): add update endpoint + label-shadow guard for project_resource (MUL-2662)
Addresses the Elon review on PR #3263:
- Add PUT /api/projects/{id}/resources/{resourceId} with sqlc query,
matching handler, CLI `project resource update`, and a new
EventProjectResourceUpdated WS event. resource_type stays immutable;
ref/label/position are all individually optional.
- Catch same-project (daemon_id, local_path) collisions where only the
embedded label differs — the row-level UNIQUE only matches the full
ref JSON, so a label typo would otherwise let the same working
directory bind twice.
- Tests cover the update lifecycle (label-only / ref / clear / 404 /
invalid path) and the label-shadow conflict on both create and
update; the in-place rename still succeeds because the conflict
scan ignores the row being edited.
Incidental: regenerating sqlc picked up a missing skills_local scan in
UpdateAgentCustomEnv that drifted in from #3200.
Co-authored-by: multica-agent <github@multica.ai>
* fix(project): close bundled-create label-shadow gap + merge resource_ref on CLI update (MUL-2662)
Two follow-ups from MUL-2662 review round 2:
- CreateProject inline resources path now dedupes local_directory entries on
(daemon_id, local_path) before opening the transaction. The DB-level
UNIQUE(project_id, resource_type, resource_ref) constraint only fires on a
full JSON match, so two rows with the same target but different `label`
would otherwise slip past. Standalone POST/PUT already cover this via
findLocalDirectoryConflict; bundled create was the missing surface.
- `multica project resource update` now seeds resource_ref from the existing
row before applying per-type shortcut flags, so `--default-branch-hint x`
on its own no longer constructs a payload missing `url` (which the server
400s on). Local_directory partial edits get the same merge behavior.
Co-authored-by: multica-agent <github@multica.ai>
* feat(desktop): local_directory project_resource UI (MUL-2665) (#3273)
* feat(desktop): local_directory project_resource UI (MUL-2665)
First UI surface for the local-working-directory flow tracked in MUL-2618.
Lets users on the desktop pin a project to an existing folder on this
machine; web stays read-only since the per-daemon check can't be done in
the browser.
What's new for the renderer:
- ProjectResourcesSection grows a desktop-only "Add local directory"
button next to the existing GitHub-repo popover. Clicking it opens
Electron's native folder picker, validates the path through a new
IPC pair (existence + r/w), and submits a project_resource of
resource_type=local_directory with daemon_id pulled live from
daemonAPI.getStatus.
- LocalDirectoryRow renders the rename pencil + path tooltip, and
greys out when ref.daemon_id != this machine's daemon_id (with a
"only available on the machine that registered this directory"
tooltip). Delete stays enabled so users can drop stale registrations
from any device.
- LocalDirectoryHint sits above the issue-detail comment composer and
shows "Agent will work in-place at {label} ({path})" when the issue's
project has a local_directory matching this daemon. Hidden on web.
- TaskStatusPill picks up a new "waiting_for_directory_release" stage
that the daemon will publish when it dequeues a task but can't
acquire the path lock. The render is in place now so the daemon
sibling subtask can wire the status string without an additional UI
PR.
Plumbing:
- @multica/core/types gains LocalDirectoryResourceRef +
UpdateProjectResourceRequest, and the api client gets the matching
PUT method backed by the server endpoint that landed in
|
||
|
|
823f124d67 |
feat(daemon): extend GC to chat / autopilot / quick-create tasks (#2260)
* feat(daemon): extend GC to chat / autopilot / quick-create tasks
Before this change the daemon's GC was strictly issue-centric: only tasks
with a non-empty issue_id ever wrote .gc_meta.json, and shouldCleanTaskDir
called only the issue gc-check endpoint. Chat / autopilot run / quick-create
tasks fell through to the GCOrphanTTL mtime path, which mis-killed active
chat sessions while leaving deleted ones around far longer than necessary.
Schema:
- GCMeta gains a Kind discriminator and per-kind ID fields
(ChatSessionID / AutopilotRunID / TaskID). WriteGCMeta now takes a
GCMeta struct so the call site classifies the task explicitly.
- ReadGCMeta defaults empty Kind to GCKindIssue, so legacy on-disk meta
files keep flowing through the issue path with no migration required.
Server endpoints (siblings of /api/daemon/issues/{id}/gc-check, all behind
requireDaemonWorkspaceAccess for the same anti-enumeration shape):
- GET /api/daemon/chat-sessions/{id}/gc-check -> {status, updated_at}
- GET /api/daemon/autopilot-runs/{id}/gc-check -> {status, completed_at}
- GET /api/daemon/tasks/{id}/gc-check -> {status, completed_at}
shouldCleanTaskDir dispatches on Kind:
- chat: active is hard-skipped (no mtime fallback) so idle sessions are
never reclaimed; archived + GCTTL cleans; 404 falls back to mtime to
stay safe for cross-workspace tokens.
- autopilot_run: terminal (completed/failed/skipped/issue_created) +
GCTTL cleans; running/pending skips. Uses run.completed_at as the TTL
anchor since autopilot_run has no updated_at column.
- quick_create: terminal task status cleans immediately (workdir is not
reused by the linked issue task, which has its own envRoot); running
skips.
Also drops the "skipping .gc_meta.json: issue_id is empty" warn — with
the new kind dispatch, chat/autopilot/quick-create tasks now write a
proper meta file instead of triggering this log.
Refs: GC follow-up to PR #2077 (symptom fix) and #2115 (chat hard delete).
Co-authored-by: multica-agent <github@multica.ai>
* fix(daemon): chat gc-check 404 cleans immediately, no mtime gate
PR review caught that the chat 404 path was routing through
orphanByMTime, which deferred reclamation to GCOrphanTTL (72h) when
acceptance #3 calls for cleanup within one GC cycle (≤ 1h) after the
user hard-deletes a session.
Every chat_session_id we ever ask about was written by this same daemon
under its current token, so the cross-workspace probe defense the issue
path needs doesn't apply here. Drop the gate and clean on 404 directly.
Test updates:
- TestShouldCleanTaskDir_KindDispatch/chat_404 flips the locked
expectation from gcActionSkip to gcActionClean.
- Adds TestShouldCleanTaskDir_ChatHardDeletedFreshMtime: GCOrphanTTL
set to a year so any mtime-based path is unmistakably out, and the
fresh-mtime workdir still cleans on the chat-404 fast path.
Co-authored-by: multica-agent <github@multica.ai>
---------
Co-authored-by: multica-agent <github@multica.ai>
|
||
|
|
1d1dedbf6e |
fix(daemon): reclaim disk on long-open issues + correct cancelled-status check (#1931)
* fix(daemon): reclaim disk on long-open issues + correct cancelled-status check Two related fixes for GitHub #1890 (self-hosted disk space growth): - The GC's done/cancelled branch compared `status.Status` against `"canceled"` (single l), but the issue schema and the rest of the daemon use `"cancelled"` (double l). Cancelled issues therefore never matched and only fell out via the 72h orphan TTL, which itself doesn't fire because cancelled issues are still reachable. Aligning the spelling lets cancelled-issue task dirs be reclaimed on the normal TTL path. - Add a third GC mode, artifact-only cleanup, for the common case the report flagged: an issue stays open for days while many tasks complete on it, so per-task `node_modules`, `.next` and `.turbo` directories accumulate without ever becoming GC-eligible. The new branch fires when `.gc_meta.completed_at` is older than `MULTICA_GC_ARTIFACT_TTL` (default 12h), the env root is not currently in use by an active task, and the issue is still alive. It removes only directories whose basename matches `MULTICA_GC_ARTIFACT_PATTERNS` (default narrow: `node_modules,.next,.turbo`); source, `.git`, `output/`, `logs/` and the meta file are preserved so subsequent tasks can still resume the workdir. Patterns containing path separators are dropped, `.git` subtrees are never descended into, symlinked matches are not followed, and every removal target is verified to live inside the task dir. Bookkeeping: `Daemon` now tracks active env roots with a refcounted set so the GC loop never reclaims a directory that is mid-execution; `runTask` claims the predicted root early plus the prior workdir on reuse paths. The cycle log is extended with bytes reclaimed and per-pattern counts so self-hosted operators can see what was freed. Docs: extend the daemon configuration table in CLI_AND_DAEMON.md with the new GC env vars and add a Workspace garbage collection section explaining the three modes and the artifact-pattern contract. Co-authored-by: multica-agent <github@multica.ai> * fix(daemon): protect active env root from full GC removal too Address GPT-Boy's PR #1931 review: the active-root guard only fired in the artifact-cleanup branch, leaving a real race on the full-removal paths. A follow-up comment on a long-done issue dispatches a task that reuses the prior workdir, but `CreateComment` does not bump issue.updated_at — so the issue still satisfies the done+stale GCTTL window and `gcActionClean` would `RemoveAll` the directory mid-execution. The orphan-404 path is similarly exposed when a token's workspace access is in flux. Move the `isActiveEnvRoot` check to the top of `shouldCleanTaskDir` so all three delete actions (clean, orphan, artifact) skip an in-use env root in one place, and drop the now-redundant guard from the artifact branch. Add tests covering the three at-risk paths: active root + done/stale issue, active root + 404 issue past orphan TTL, active root + no-meta orphan past TTL. Also align two stale comments noted in the same review: cleanTaskArtifacts now documents that symlinks are skipped entirely (the previous note implied the link itself was removed), and GCOrphanTTL no longer claims that 404s are cleaned immediately — the implementation gates them on the same TTL. Co-authored-by: multica-agent <github@multica.ai> --------- Co-authored-by: multica-agent <github@multica.ai> |
||
|
|
8f10741a4d |
feat(daemon/gc): tighten GC defaults + flex duration suffix (#1559)
* feat(daemon/gc): tighten GC defaults + flex duration suffix Driven by user feedback in #1539 (40 GB VPS filling within 24h of heavy AI-coding usage): the existing TTLs were sized for desktop/laptop deployments and are too lenient for small-disk, long-running daemons. - GCTTL: 5d → 24h. Done/canceled issues almost never need a multi-day grace period in AI-coding workflows. - GCOrphanTTL: 30d → 72h. Covers crash-leftover and pre-GC directories without a month-long wait. - Issue-deleted orphans (API returns 404) are now cleaned on the next GC cycle regardless of mtime. The issue row is gone; there is nothing left to protect. - parseFlexDuration: accept a `d` (day) suffix in addition to the stdlib time.ParseDuration syntax. MULTICA_GC_TTL=5d now works; previously only 120h was accepted. * fix(daemon/gc): address review — 404 safety + decimal/overflow in duration parser Two issues flagged in PR review: 1. 404-immediate-clean is unsafe. The /gc-check endpoint returns 404 for both "issue deleted" AND "daemon token has no access to the workspace" (anti-enumeration, see requireDaemonWorkspaceAccess). Clean-on-404 would let a scoped-down daemon token wipe taskDirs whose issues are still live. Restore the mtime gate against GCOrphanTTL. With the new 72h default we still shrink the original 30d window dramatically without the cross-workspace hazard. Lock the behavior in with a new test that asserts a recent 404 is skipped. 2. parseFlexDuration mishandled decimals and swallowed Atoi errors: "0.5d" → 7m12s (regex matched only the "5d"), "1.5d" → 1h7m12s, and 20+ digit day values Atoi-errored silently to 0. Match the full decimal number with `\d*\.\d+|\d+` and parse with ParseFloat so fractional days and oversized inputs both go through time.ParseDuration correctly — fractions as sub-hour durations, overflow as a returned error. |
||
|
|
2f1000d815 |
merge: resolve conflict with main (runTask refactor + mergeUsage)
Main introduced executeAndDrain/mergeUsage refactor. Resolve by keeping main's refactored structure and re-applying EnvRoot to the switch/case in runTask. Rename newTestDaemon → newGCTestDaemon to avoid collision with the helper added in daemon_test.go on main. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> |
||
|
|
20809052f5 |
fix(daemon): address GC review feedback
- Move WriteGCMeta from runTask() to handleTask() so it runs after task completion, not at start. Mid-task crashes leave orphan dirs that get cleaned by GCOrphanTTL. - Strengthen isBareRepo to check both HEAD and objects/ directory. - Remove empty workspace directories after all task dirs are cleaned. - Add 30s context timeout to git worktree prune to prevent hangs. - Add comprehensive unit tests for shouldCleanTaskDir (8 scenarios), cleanTaskDir, gcWorkspace empty-dir cleanup, isBareRepo, and WriteGCMeta/ReadGCMeta roundtrip. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> |