Commit Graph

191 Commits

Author SHA1 Message Date
Carlos Florêncio 8ca01e0f7e feat(git): add local-first contribution resolution (#2560) 2026-08-12 19:12:35 +01:00
José Almeida 146419fc8c fix(search): include workspace root with submodules (#2524) 2026-08-12 10:53:07 +01:00
Carlos Florêncio c4b4791de6 fix(git): contain rewritten contribution histories (#2509) 2026-08-12 08:12:53 +01:00
João Salavisa c5c591b60b refactor(pty): collapse duplicated PTY and cmdline helpers into common/ptyexec (#2457)
Co-authored-by: Kandev Agent <agent@kandev.dev>
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-09 01:03:13 +01:00
João Salavisa 1afc96ea41 fix(agentctl): size file-search results from trusted bounds (#2434)
Co-authored-by: Kandev Agent <agent@kandev.dev>
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-08 19:38:34 +02:00
João Salavisa 70e6eb8895 fix(git): count diff lines by hunk position, not by "+++"/"---" prefix (#2427)
Co-authored-by: Kandev Agent <agent@kandev.dev>
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-08 19:17:09 +02:00
Carlos Florêncio 33ecd0d945 fix(agentctl): log canceled git status refreshes at debug (#2398) 2026-08-07 21:01:47 +01:00
Nicolas Lenepveu a35dd6d22e fix(passthrough): deliver prompt to custom TUI agents via stdin (#2335) 2026-08-07 15:24:58 +01:00
Carlos Florêncio a5d6649f69 feat(review): support nested submodule review (#2295) 2026-08-06 15:04:19 +01:00
nova28 d15d5da8fa fix(backend): seed every workspace with its stored base branch (#2270) 2026-08-05 22:37:07 +01:00
Carlos Florêncio 84fe8132a1 feat: support remote contribution tasks (#2265) 2026-08-05 19:25:58 +01:00
Carlos Florêncio a7b416a1a8 fix(web): retire unreachable archived sidebar filter (#2246) 2026-08-05 19:06:49 +01:00
José Almeida 58f0f6214e feat(lsp): run language servers on task hosts (#1863) 2026-08-05 10:57:24 +01:00
Carlos Florêncio 3f46da6645 fix(backend): harden office-disabled cron, title limits, and dead-runtime cleanup (#2206) 2026-08-03 15:11:58 +01:00
Carlos Florêncio 431e5d40fe fix: enforce classified Git admission paths (#2150) (#2181) 2026-08-03 11:04:34 +01:00
Carlos Florêncio 75cb354fbe fix: isolate high-volume session stream traffic (#2175) 2026-08-03 10:07:12 +01:00
Carlos Florêncio b5631da7ee feat: surface OpenCode provider limit errors (#2167) 2026-08-02 21:48:14 +01:00
Alassane 3c7ae1ab9a fix(gitlab): re-land push-detection auto-link fix dropped by #2124's squash merge (#2172) 2026-08-02 21:31:23 +01:00
Carlos Florêncio 6fa13623c2 fix: show files for merge commit details (#2173) 2026-08-02 21:03:40 +01:00
João Manso 46a35704ec fix(agentctl): demote unwatched workspace trackers to slow polling (#2113) 2026-08-02 14:37:50 +01:00
Carlos Florêncio 27e7e0776a fix: preserve managed GitHub tools across login shells (#2141) 2026-08-02 11:18:32 +01:00
João Manso 3b91515f8f perf(agentctl): collapse the git poll tick into a single spawn (#2133) 2026-08-01 22:02:03 +01:00
José Almeida 03e179c4d3 feat: add last task startup preference (#2102) 2026-08-01 09:58:05 +01:00
Carlos Florêncio ea5173c922 fix: re-anchor stale git base for commits panel and cumulative diff (#2062) 2026-07-31 14:53:36 +01:00
Clément Fiere 59d1cc7277 feat: add Slack-style unread divider to session transcript (#1922) 2026-07-31 14:42:14 +01:00
João Salavisa 7c73d5c4c4 fix(agentctl): tear down adapter when the agent process self-exits (#2095)
Co-authored-by: Kandev Agent <agent@kandev.dev>
2026-07-31 14:06:06 +01:00
João Salavisa 6c01e4e1ae fix(agentctl): clamp file-search limit to bound result allocation (#2088)
Co-authored-by: Kandev Agent <agent@kandev.dev>
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-31 09:31:37 +01:00
Carlos Florêncio 167ab6b778 feat(mcp): add session attachment diagnostics (#2061) 2026-07-30 20:49:07 +01:00
José Almeida 7ec82dcaec feat(search): add task workspace search palette (#2006) 2026-07-29 09:50:29 +01:00
Carlos Florêncio 09b5c4df7a feat: propagate managed git environment to terminals (#2010) 2026-07-28 21:19:27 +01:00
João Salavisa 16495a6dcc fix(terminal): expose executor-profile env vars to terminal shells (#1992)
* fix(terminal): expose executor-profile env vars to terminal shells

PR #1971 got the profile env into the agent subprocess and the repository
setup script, but the terminal panel was still a gap: the local user shell
was started with `InteractiveStartRequest.Env` unset, so it inherited only
the backend process environment, and agentctl's per-terminal shells were
started from `shell.DefaultConfig` with no env overrides at all.

Local/worktree path: resolve the task environment's executor profile at
terminal start (revealing secret-backed entries) and pass it through
UserShellOptions.Env into the deferred PTY start. Resolution happens per
terminal rather than being cached at launch, so a terminal opened before
the agent ever runs still gets the vars.

Remote path (Docker/Sprites/SSH): StartTerminalShell now folds the agent
instance's env into the shell config, so a terminal inside the workspace
sees what the agent sees. SSH is unchanged in scope — its instance env is
still only the credential allowlist, not arbitrary profile variables.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* fix(terminal): resolve terminal env from the session's executor profile

Address PR review. The resolver read executor_profile_id off the
task_environments row, but persistTaskEnvironment's reuse branch never
refreshes that column — it keeps whatever the first session stamped on it.
buildLaunchAgentRequest resolves the agent's env from
session.ExecutorProfileID instead, so a task environment reused by a later
session that picked a different profile of the same executor type would give
the terminal stale secrets and omit the ones the agent just received.

Prefer the session's profile and fall back to the environment row only when
the session recorded none. Also document why mergeAgentEnvIntoShellConfig
needs no defensive copy and why StartEnvForTesting is exported.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* test(terminal): give env-row fallback subtests distinct session IDs

Address PR review nit: both subtests shared the hardcoded "session-3" at
the call site, so the error-path case read as a repeat of the first.
Move the session ID into the table as session-no-profile / session-error.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Kandev Agent <agent@kandev.dev>
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-28 00:08:57 +01:00
João Salavisa bb72d70586 refactor(backend): remove dead code and deprecated shims (#1965)
Delete unreachable/unused backend symbols confirmed by staticcheck U1000
and golang.org/x/tools/deadcode, plus deprecated back-compat aliases kept
alive only by their own tests.

Removed (unused):
- parseGitHubRepoURL, syncTaskStateForBulkWorkflowMove (task/service)
- resolvePrimaryRepoInfo (orchestrator/executor)
- resolveWorkflowAgentProfile, workflowDefaultAgentProfile wrappers (mcp/handlers)
- cleanupExitedContainer (runtime/lifecycle) + orphaned fmt import
- VscodeManager.setEnv (agentctl/server/process)
- repoRootDir const (cmd/preview)

Removed (deprecated shims):
- Canceller.CancelSessionAndNotify alias -> callers use DetachSessionAndNotify
- ScriptPlaceholder type alias (live scriptPlaceholders var kept)
- Placeholder{QueueRun,ClearDecisions,QueueRunForEachParticipant}Callback
  no-op structs and their self-referential test

No behavior change; build, vet, and affected-package tests pass.

Co-authored-by: Kandev Agent <agent@kandev.dev>
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-26 17:54:12 +01:00
Carlos Florêncio bc218dfbbe feat: add workspace sources from files panel (#1900)
* feat: add workspace sources from files panel

* fix: preserve worktree identity during recovery

* feat: unify workspace source actions

* fix: validate recovered workspace repositories

* fix: harden workspace source filesystem boundaries

* test: split workspace repository chip coverage

* fix: restrict repository rescan recovery paths

* fix: stabilize workspace source CI checks

* test: canonicalize workspace source paths

* test: align workspace source container paths

* fix: populate direct workspace repository names

* test: preserve git fixture rewrites across restarts

* test: isolate backend git fixture config

* fix: resume remote workspace executions before creation

* fix: preserve ssh agent identity during resume

* fix: preserve SSH workspace across backend restart

* fix: simplify workspace actions trigger

* fix: refine add workspace repositories UX

* docs: narrow workspace source UX updates

* fix: avoid phantom turn on session resume

* test(web): normalize workspace source request headers

* feat(web): streamline workspace source picker

* docs: remove trailing spaces from workspace source plan

* fix: prevent resume status from creating phantom turns

* fix(web): stabilize workspace source attachment

* refactor(web): extract workspace source store contract

* fix: avoid phantom turns on idle agent resume

* fix: address backend static analysis findings

* fix: preserve agentctl readiness status value

* fix: preserve resume messages without active turns

* fix: resume agents from promoted workspace root

* chore: merge origin/main into feature/add-new-repo-folder-d97

* feat(web): explain workspace source consequences

* test(websocket): count session activity subscription
2026-07-25 21:29:53 +01:00
Carlos Florêncio 2f6be0c777 fix(agents): harden ACP launcher configuration (#1917) 2026-07-24 16:36:44 +01:00
Jérémy Coatelen cd332ef26b feat(task): nest a task under another as a sub-task from the sidebar (#1837) 2026-07-24 09:51:45 +01:00
Carlos Florêncio 8c010ebeca fix: inherit service temporary environment (#1894) 2026-07-23 17:24:54 +01:00
Carlos Florêncio 258896622b fix: hide workspace ownership marker (#1874) 2026-07-23 14:44:34 +01:00
Carlos Florêncio 7fd99c3f33 feat: complete GitLab integration parity (#1832) 2026-07-21 23:24:22 +01:00
Carlos Florêncio f212258907 perf: scale workspace git status for large worktrees (#1814) 2026-07-20 11:04:59 +01:00
Carlos Florêncio 1dc96a92c6 fix: restore multi-repo review diffs (#1795) 2026-07-19 12:25:36 +01:00
Carlos Florêncio 3e68fd3da6 fix: prevent stale ready events from completing replacement turns (#1678) 2026-07-14 13:20:52 +01:00
Carlos Florêncio 8a2404251d fix: guard agent profile orphan cleanup (#1659) 2026-07-11 12:31:21 +01:00
jijiamoer 5c0d4f5e9b feat: add Devin ACP agent integration (#1497) 2026-07-02 21:57:25 +01:00
Carlos Florêncio 49c482bd4d fix: prevent leaked agent processes on shutdown (#1498) 2026-06-26 10:09:23 +01:00
Clément Fiere 18366382b4 fix(backend): expand ~ home links and report missing file paths cleanly (#1468) 2026-06-23 12:13:46 +01:00
José Almeida 2781556ead perf: tame browser-grinding cumulative diffs during large rebases (#1430)
* perf(backend): cap cumulative diff bytes and file count during large rebases

* perf(web): coalesce cumulative-diff refetches and skip redundant status compares

* feat(web): warn when the changes panel hides files from a capped diff

* fix(backend): enforce cumulative diff total byte budget strictly

* fix(web): keep coalesced refetch timer shared across envKey subscribers

* fix(web): drop unsound git-status SHA fast-path that staled live diffs

* fix(web): cancel coalesced timer on mid-flight drain to avoid duplicate refetch

* refactor(web): move changes-panel banner and helper to a sibling file

* docs(backend): clarify cumulative diff truncated-files-count semantics

* fix(web): tighten useSelectedFileKey mode param to the all|file union
2026-06-18 17:28:21 +01:00
Clément Fiere a71ee2e2c1 fix: open agent file links that carry line-range selectors (#1412) 2026-06-17 20:32:40 +01:00
Carlos Florêncio cf3469b4d1 feat: allow read-only absolute file paths (#1371) 2026-06-14 19:32:36 +01:00
Carlos Florêncio 326297c3e7 fix(agent): avoid oversized env spawn failures (#1344) 2026-06-11 22:25:58 +01:00
João Salavisa d83c1dd982 feat(diff): use stored base_branch for diff stats + per-task compare picker (#1273)
* feat(diff): use stored base_branch for diff stats + per-task compare picker

Task-card additions/deletions and the Changes panel were computed against a
hardcoded origin/main → master priority list inside agentctl. For tasks
branched off a release/staging branch this inflated the counts by every
commit since the actual fork point, and the Changes panel grouped unrelated
files alongside the task's real work.

This wires the per-repo task_repositories.base_branch end-to-end into the
agentctl WorkspaceTracker so each repo's diff stats resolve against the
recorded base instead of the integration-branch fallback. A picker on the
Changes panel branch hover card lets the user change the base mid-task; the
new value is persisted via PATCH /tasks/:id/repositories/:repo_id, pushed
live to the running agentctl instance, and the session's cached
base_commit_sha is cleared so the commits panel + cumulative diff refetch
against the new base without a session restart.

Falls back to the existing origin/main → master list for legacy tasks
(empty base_branch) and to the branch tip when no merge-base exists with
HEAD (unrelated histories — local rebase backups, etc.) so the stats and
commits panels stay anchored on the same SHA in every failure mode.

* fix(agentctl): sanitize user-controlled base_branch refs before git exec

CodeQL flagged the picker's user-controlled base_branch payload reaching
exec.Command("git", ..., baseBranch) inside workspace_git_cmd.go as a
command-built-from-user-controlled-source vulnerability. A malicious caller
could smuggle a leading-"-" argument like "-upload-pack=..." that git
itself reinterprets as a flag, even though exec.Command keeps args
separate from the shell.

Adds an IsSafeGitRef sanitizer at every boundary the value crosses on its
way to git: the service write path, the agentctl HTTP handlers
(/workspace/base-branches, /git/log, /git/cumulative-diff), and
WorkspaceTracker.SetBaseBranch itself. Unsafe values are rejected at the
service tier and silently downgraded to the no-override fallback inside
agentctl so a misbehaving caller can't half-break the tracker.

* fix(diff): address PR review blockers and CI lint

- Split UpdateRepositoryBaseBranch into validate/load/apply helpers to drop
  cyclomatic complexity below the cyclop limit that gated Run Backend Tests.
- Replace direct m.cfg.BaseBranches reads/writes with getBaseBranches /
  setBaseBranches under a dedicated RWMutex. UpdateBaseBranches used to
  write under repoTrackersMu while rescan + lazy-subpath callers read under
  workspaceTrackersMu, leaving an unsynchronized data race on the map.
- Drop the false 'PR target is auto-wired' claim from the MCP tool
  description — GitOperator.CreatePR takes baseBranch as an explicit arg
  and never reads the tracker's value.
- Picker no-op guard now compares against currentBase instead of storedBase
  so selecting the displayed fallback (legacy task before any picker
  change) is treated as no-op instead of triggering a spurious save.
- HTTP handler now maps non-validation service errors to 500 instead of
  400, classifying via the existing isValidationError helper plus the new
  unsafe-git-ref message.

* fix(diff): regex-based ref sanitiser at every git subprocess sink

CodeQL kept flagging workspace_git_cmd.go:43 even after SetBaseBranch
rejected unsafe values — the taint tracker doesn't follow sanitised stores
through struct fields, so it still saw user input flowing into
exec.Command("git", ..., baseBranch).

Switches IsSafeGitRef to a single regex check (a pattern CodeQL recognises
as a sanitiser barrier) and adds SanitizeGitRef calls inline at every
agentctl call site that splices a user-controlled ref into a git arg:

  - workspace_git_status.resolveStoredRef (before the rev-parse probes)
  - workspace_git_status.computeBaseCommit (before merge-base/rev-parse)
  - workspace_git_status.getAheadBehindCounts (before rev-list)
  - agentctl/server/api/git.go runGitLogForRepo + handleGitCumulativeDiff
    (before computeMergeBase/GetRevParse on the HTTP TargetBranch)

Behaviour is unchanged for valid refs — only narrows the allowlist (now
[A-Za-z0-9_./-]) so the sanitiser barrier is recognisable to static
analysis. SetBaseBranch's field-store sanitiser stays as defence in depth.

* fix(diff): transform sanitiser at every map read for CodeQL taint flow

CodeQL still flagged workspace_git_cmd.go:43 because:
- handleSetBaseBranches used an IsSafeGitRef bool guard before a verbatim
  map[k] = v copy. CodeQL recognises regex-transforming sanitisers but not
  bool-guarded passthroughs.
- lookupBaseBranch returned m.cfg.BaseBranches[k] verbatim. Even though
  the map was sanitised on write, taint flow across map stores + field
  stores loses the sanitised state in the analyser.

Switches both sites to the transforming SanitizeGitRef helper so every
read produces a freshly-sanitised value before it flows to a downstream
git argument. Behaviour is unchanged for valid refs.

* fix(diff): regex transformer sanitiser + guard live push from empty map

CodeQL still flagged the previous SanitizeGitRef because it returned the
input string unchanged on success — an identity passthrough that the taint
tracker doesn't recognise as a sanitiser barrier.

Switches to safeGitRefPattern.FindString so the output comes from the
regex match (not the parameter). IsSafeGitRef now wraps SanitizeGitRef so
the predicate and the transformer share one allowlist.

Also addresses two Claude review blockers:

- collectTaskBaseBranches returns a nil/empty map when the task has no
  recorded base_branches; pushing that to agentctl wipes every tracker's
  override. Now skipped — the DB row is the source of truth for the next
  session launch.

- applyBaseBranchSideEffects now runs on context.WithoutCancel so an HTTP
  / WS request cancel after the task_repositories UPDATE commits can't
  leave session.base_commit_sha stale + the live agentctl tracker map
  un-pushed, drifting from the persisted row.

* fix(diff): share securityutil.IsValidBranchName + harden error classification

CodeQL still flagged the picker's regex sanitiser as taint passthrough.
Refactors SanitizeGitRef / isSafeBaseBranchRef to delegate to the existing
securityutil.IsValidBranchName helper that agentctl's Rebase / Merge /
RenameBranch already use without being flagged. The shared allowlist also
fixes a Claude review blocker: 'feature/@2024' used to pass the service
validator but get dropped by the agentctl-side check at push time,
silently wiping every tracker's override.

Plus the two error-classification blockers Claude raised on the previous
HEAD:

- WS handler (task_ws_handlers.go wsUpdateTaskRepository) now distinguishes
  validation errors (surfaced verbatim) from internal faults (opaque
  500-style message). HTTP path already did this via the existing
  isValidationError helper.
- MCP handler (handlers.handleUpdateRepositoryBaseBranch) gets the same
  treatment via a new isValidationError local to the mcp/handlers
  package so raw service error strings stop leaking across the MCP
  boundary.

* fix(diff): inline securityutil.IsValidBranchName at every git subprocess sink

CodeQL's interprocedural taint tracking wasn't recognising SanitizeGitRef
as a sanitiser even after it delegated to securityutil.IsValidBranchName
— the wrapper helper + map/field hops between source and sink kept the
flow tainted in the analyser.

Inlines the IsValidBranchName allowlist check (already used inline by
Rebase/Merge in git.go without being flagged) at every call site that
splices a user-controlled ref into a git subprocess argument:

- workspace_git_status.resolveStoredRef (before rev-parse --verify)
- workspace_git_status.computeBaseCommit (before merge-base / rev-parse)
- workspace_git_status.getAheadBehindCounts (before rev-list)
- agentctl/server/api/git.go runGitLogForRepo + handleGitCumulativeDiff
- agentctl/server/api/workspace_base_branches.go handleSetBaseBranches

Behaviour unchanged: same allowlist as Rebase / Merge / RenameBranch
already use. SanitizeGitRef / IsSafeGitRef survive as defence-in-depth
helpers for callers that want a one-line check; they delegate to the same
securityutil helper so the rules stay aligned.

* fix(diff): inline regexp.MatchString at every git subprocess sink

CodeQL still flagged workspace_git_cmd.go:43 even with the inline
securityutil.IsValidBranchName helper — the taint tracker apparently does
not follow into helper functions for the go/command-injection rule on
this path.

Hoists the regex to package-scope vars (safeBranchRefPattern in both
process and api packages) and calls regexp.MatchString directly at each
sink site. Behaviour is identical to securityutil.IsValidBranchName; only
the call form changes.

* fix(diff): SHA-shape allowlist on BaseCommit before downstream git diff

CodeQL still flagged workspace_git_cmd.go:43 because update.BaseCommit
is the OUTPUT of an earlier merge-base / rev-parse call whose argument
list contained a user-controlled branch ref — the analyser treats that
command's output as still tainted even when the input ref was sanitised.

Adds an inline 40-hex SHA-shape allowlist check in enrichWithBranchDiff
right before `git diff --numstat <update.BaseCommit>` so the sanitiser
barrier sits in the same function as the next subprocess call. Behaviour
is unchanged: merge-base / rev-parse already produce SHA-shaped output.

* fix(diff): async tracker refresh + tighten init spread order

Two CodeRabbit/Claude review fixes:

- UpdateBaseBranches stamps each tracker's new baseBranch synchronously
  then runs RefreshGitStatus on a background goroutine instead of inside
  the picker-save HTTP handler. Per-tracker git subprocesses can each
  take ~3-5 calls; serialising them on the request goroutine made the
  PATCH response wait for every refresh to finish. The picker UI re-fetches
  via the existing WS stream once Refresh emits, so the handler doesn't
  need to block.

- updateTaskRepositoryBaseBranch now spreads options.init BEFORE the
  required method/body fields so an upstream caller's init overrides
  can't replace the PATCH method or the base_branch payload. Matches
  the convention deleteTask / moveTask already follow.

* fix(diff): narrow mcp isValidationError + opaque 500 + post-sanitize log count

Three final Claude review threads on the latest commit:

- mcp/handlers isValidationError drops the broad 'invalid' substring so
  DB / IO errors whose messages happen to contain 'invalid' don't
  misclassify as ErrorCodeValidation.

- task_http_handlers httpUpdateTaskRepository now logs the raw service
  error server-side and returns an opaque 'failed to update task
  repository' on the 500 path. Stops potential DB / IO detail leakage
  through the HTTP boundary.

- workspace_base_branches debug log reports accepted + rejected counts
  (post-sanitisation) instead of the pre-filter total so the metric
  actually reflects what hit UpdateBaseBranches.

---------

Co-authored-by: Kandev Agent <agent@kandev.dev>
2026-06-05 15:53:39 +01:00