Skip to content

Embedded agent still sandboxed to a single MFE's projectRoot — cross-MFE agentRoot work is implemented but not wired/released #55

Description

@kstohrer

Embedded agent still sandboxed to a single MFE's projectRoot — cross-MFE agentRoot work is implemented but not wired/released

Summary

In a single-spa / micro-frontend setup where the root shell and every MFE each
run their own Vite dev server (annotask({ mfe, server }) pointed at the root
server, per the docs), a task whose target file lives in a sibling MFE
still fails with a permission/access error when the embedded agent tries to
read or edit it — even though:

  • the consuming repo's MFE dev-server configs already POST their identity to
    the root server on boot (registerWithRootServerPOST /__annotask/api/mfes/register) via the documented annotask({ mfe, server }) options, and
  • a pnpm-workspace.yaml was added at the monorepo root specifically to give
    resolveWorkspace() a workspace root to discover.

Neither fix helps, because the embedded coding-agent CLI (claude/codex/
opencode/copilot) is still spawned with cwd: projectRoot
— the single
MFE's own directory — not the broader agentRoot the registry is designed to
compute. The spawned CLI enforces its own filesystem sandbox relative to that
cwd, so bash/glob/view calls against ../mfes/<sibling>/... are refused
by the CLI itself, independent of anything Annotask's own HTTP API permits.

  • Affects: annotask@0.4.5 (latest published; confirmed via installed
    node_modules/annotask in a consumer repo — zero references to
    mfe-registry, computeAgentRoot, mfes/register, or agentRoot
    anywhere in dist/)
  • Area (this repo, local main checkout): src/server/mfe-registry.ts,
    src/server/agent-spawn.ts, src/server/api.ts

Steps to reproduce (consumer repo)

  1. A monorepo-style layout: apps/root (shell, runs the Annotask server) +
    apps/mfes/<name> (several sibling MFEs), each MFE's vite.config.ts
    passing annotask({ mfe: '@org/<name>', server: 'http://localhost:5174' })
    pointed at the root's dev server port.
  2. Run the root shell + at least one MFE locally (e.g. via a script that
    cds into each package and runs vite).
  3. Open the Annotask UI at the root's origin, select an element that's
    actually rendered by the sibling MFE (not the shell), and ask the embedded
    agent to make an edit.
  4. The agent locates the file's path correctly (it's stamped in the task's
    anchor / grounding data) but reports something like: "Access to
    ../mfes/<name>/ is blocked in this sandbox — my tools only permit
    reads/writes within <root's own projectRoot> and its children... every
    attempt returns 'Permission denied'."

Expected

Per this repo's own src/server/mfe-registry.ts doc comment:

From the union of the root projectRoot + every registered MFE root we
derive the agent root — their nearest common ancestor — which becomes:

  • the embedded agent's spawn cwd (so it can read/write every MFE), and
  • the path-containment root for cross-MFE file resolution

i.e. the spawned CLI's cwd should be the computed agentRoot, not the
single MFE's projectRoot, whenever one or more MFEs have registered.

Actual

  • src/server/agent-spawn.ts (handleSpawn) still spawns with a hardcoded
    cwd: projectRoot:
    child = (opts.spawnImpl ?? spawn)(parsed.cli, parsed.args, {
      cwd: projectRoot,
      shell: false,
      ...
    })
    — this is the published 0.4.5 behavior. On the local main checkout,
    this has actually already been fixed: src/server/api.ts now resolves
    const agentRoot = await getAgentRoot(options.projectRoot) and calls
    options.agentSpawn.handleSpawn(req, res, parsed.data, agentRoot) (api.ts
    ~L1179-1180), so the spawn cwd is the computed agent root there.
  • However, src/server/mfe-registry.ts and its test file are untracked
    (git status shows ??)
    — this work has not been committed, let alone
    released. It is not in CHANGELOG.md (latest entry is 0.4.4, which
    predates this file), and the installed 0.4.5 tarball has none of it.
  • So today, every consumer on the published package hits the sandbox error
    above with no workaround — pnpm-workspace.yaml / resolveWorkspace()
    only ever fed the component-scanning / path-containment side (already
    present since 0.4.2's "Cross-MFE apply and byte-exact undo"), never the
    actual agent spawn cwd.

Ask

  1. Commit src/server/mfe-registry.ts + its test, and confirm api.ts's
    existing getAgentRoot(options.projectRoot)handleSpawn(..., agentRoot)
    wiring is complete (it looks done from a read-through, but hasn't been
    exercised against a real multi-MFE consumer since it's uncommitted).
  2. Add a CHANGELOG entry and cut a release once verified — consumers can't
    get this fix any other way; there's no client-side or config-only
    workaround given the sandbox lives inside the spawned CLI process itself.
  3. Sanity-check computeAgentRoot's MAX_ANCESTOR_LEVELS ceiling against a
    real layout like src/frontends/{root,mfes/*,shared/*} (root and its MFEs
    share a grandparent two levels up) so a legitimate registration isn't
    silently ignored as "too broad."
  4. Once released: no consumer-side change should be needed beyond bumping
    the annotask dependency — the annotask({ mfe, server }) options and
    POST /api/mfes/register call are already wired correctly on the plugin
    side and were confirmed present in every MFE's vite.config.ts in the
    consumer repo that hit this.

Workaround in the meantime

None found. The pnpm-workspace.yaml addition and the .annotask/agents.json
projectDirections update (telling the agent sibling MFEs exist) both help
the agent reason about where files live, but can't override the hard
process-level sandbox the spawned CLI itself enforces from cwd. Cross-MFE
edits currently require running a separate Annotask/agent session rooted at
(or above) the target MFE directly.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions