Skip to content

feat(platform): agent mounts, one durable folder per agent#5247

Closed
mmabrouk wants to merge 9 commits into
agent-mounts-basefrom
feat/agent-mounts
Closed

feat(platform): agent mounts, one durable folder per agent#5247
mmabrouk wants to merge 9 commits into
agent-mounts-basefrom
feat/agent-mounts

Conversation

@mmabrouk

@mmabrouk mmabrouk commented Jul 12, 2026

Copy link
Copy Markdown
Member

Context

An agent has nowhere to keep a file between runs. Session mounts (#5197) give each
conversation a scratch folder, but that folder dies with the session. So a skill the agent
writes, a note it takes, or an artifact it builds is gone the moment the conversation ends.

This gives every agent one durable folder, backed by S3, that outlives every run and every
session. It is keyed by the agent's artifact id, mounted into the sandbox next to the working
directory, and browsable in the Session Inspector. No database migration. No /run protocol
change. No agent-template change.

What the agent sees

The runner mounts the folder at <cwd>-agent and makes it findable without editing the
author's agent.md. An agent that lists its working directory now sees:

agent-files -> /.../<cwd>-agent     a symlink into the durable folder

Open the link and the folder holds a README.md that states the one rule: files here persist
across every run and session; the working directory does not. The daemon also gets
AGENTA_AGENT_MOUNT_DIR pointing at the same path. Three signals, zero instructions text.

How it works

  • API (feat(api)): two endpoints beside the session-mount ones. sign mints a reserved
    slug from the artifact id, upserts the mount, and returns scoped S3 credentials. query
    looks one up and never creates it.
  • Runner (feat(runner)): a new agent-mount.ts module, plus wiring in sandbox_agent.ts
    that mirrors the durable session-cwd lifecycle. On every run that carries an artifact id, the
    runner signs, mounts at <cwd>-agent, seeds the README, links agent-files, exposes the env
    var, and unmounts on teardown.
  • Web (feat(web)): an "Agent files" panel in the Session Inspector's Mounts tab.

What to look at as a reviewer

I left inline comments on the parts that are easy to misread. The three that carry the most risk:

  1. Nothing here can fail a run. Every agent-mount step is best-effort: a missing artifact
    id, an unconfigured store, a failed sign, or a geesefs hiccup logs and continues. The durable
    session cwd keeps its exact behavior; the agent mount only adds to it.
  2. The Daytona env var takes a different path than the local one. The review caught a real
    bug here: the Daytona daemon reads its environment from piExtEnv at sandbox-create time, not
    from the local env object. The inline note walks through the fix.
  3. The discovery symlink self-heals. geesefs does not persist a symlink across a remount; it
    silently degrades to an empty file. So the runner rebuilds the link each run, and the env var
    and README carry the durable signal.

Tests

  • API: 36 unit + 4 acceptance. From api/:
    uv run --no-sync pytest oss/tests/pytest/unit/mounts oss/tests/pytest/acceptance/mounts -q.
  • Runner: 17 unit. From services/runner/: pnpm vitest run tests/unit/agent-mount.test.ts.
    Full runner suite regression-clean.
  • Web: vitest on src/components/SessionInspector/api.test.ts.
  • Live on the dev stack: signed, mounted, seeded, and read a file back across an unmount and
    remount. Data persists; sign is idempotent per agent and isolated across agents.
  • Daytona, verified live: an agent ran twice on Daytona (eu.preview key). The mount, README,
    symlink, cross-run persistence, and AGENTA_AGENT_MOUNT_DIR reaching the Daytona daemon all
    passed. See the QA comment below for the details.

What to QA

  • Sign a mount: POST /mounts/agents/sign?artifact_id=<uuid>&name=default with a RUN_SESSIONS
    key returns credentials scoped to your project. Sign the same id again and you get the same
    mount; sign a different id and you get a different one. A non-UUID id returns 422.
  • Open the Session Inspector on an agent session and go to the Mounts tab. The "Agent files"
    panel lists the folder's contents, or shows an empty state when the folder is new.
  • Regression: run an agent with no artifact id. It behaves exactly as before, with no agent mount.

About the base branch

This is the code. The design docs and open-issues log live in the design PR #5215.

The branch depends on two branches that have not merged and that no single branch combines: the
web panel builds on #5204, and the runner wiring sits on the runner changes in #5244. To hold the
diff to the 21 files that are actually mine, the base agent-mounts-base is a snapshot of the
current integrated workspace. Review against it, but do not merge against it. Once #5204 and
#5244 land, this rebases onto the trunk.

Claude-Session: https://claude.ai/code/session_01UogudBC9VRCRkpT5bZVxex

@vercel

vercel Bot commented Jul 12, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
agenta-documentation Ready Ready Preview, Comment Jul 13, 2026 1:39am

Request Review

@coderabbitai

coderabbitai Bot commented Jul 12, 2026

Copy link
Copy Markdown

Review Change Stack

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 7bf0ead8-443d-459b-b2a6-95b10ccd6572

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Adds artifact-scoped agent mounts across the backend, runner, and web inspector. Mounts are deterministically keyed by artifact UUID and name, mounted into sandbox environments, exposed through API endpoints, and displayed in SessionInspector.

Changes

Agent mounts

Layer / File(s) Summary
Agent mount design contract
docs/design/agent-workflows/projects/agent-mounts/*
Documents slug formats, API semantics, runner integration, frontend behavior, QA status, and deferred work.
Backend agent mount API and persistence
api/oss/src/apis/fastapi/mounts/*, api/oss/src/core/mounts/*, api/oss/src/dbs/postgres/mounts/dao.py
Adds deterministic artifact slugs, create/query services, permission-gated endpoints, UUID validation, and active mount lookup by slug.
Backend validation
api/oss/tests/pytest/acceptance/mounts/test_agent_mounts_basics.py, api/oss/tests/pytest/unit/mounts/test_agent_mounts.py
Tests validation, idempotent creation, name symmetry, archival, reserved slugs, and no-create queries.
Runner agent mount lifecycle
services/runner/src/engines/sandbox_agent*
Signs credentials, mounts local or Daytona storage, injects the mount path, seeds README files, repairs symlinks, and cleans up mounts.
Runner validation
services/runner/tests/unit/agent-mount.test.ts
Tests signing, credential failures, README seeding, symlink repair, and remote helper commands.
SessionInspector integration
web/oss/src/components/SessionInspector/*, web/oss/src/components/AgentChatSlice/*, web/packages/agenta-entities/src/workflow/state/molecule.ts
Propagates artifact IDs, queries agent mounts, refreshes related data, and renders an Agent files panel.
Frontend API validation
web/oss/src/components/SessionInspector/api.test.ts
Tests agent mount request parameters and empty responses.

Estimated code review effort: 4 (Complex) | ~60 minutes

Sequence Diagram(s)

sequenceDiagram
  participant AgentRunner
  participant MountsAPI
  participant MountStorage
  participant SessionInspector
  AgentRunner->>MountsAPI: Sign artifact mount credentials
  MountsAPI->>MountStorage: Create or fetch deterministic mount
  MountStorage-->>MountsAPI: Mount credentials or mount record
  MountsAPI-->>AgentRunner: Mount response
  SessionInspector->>MountsAPI: Query artifact mount
  MountsAPI-->>SessionInspector: Mount record or empty result
Loading

Possibly related PRs

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 23.08% which is insufficient. The required threshold is 60.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Title check ✅ Passed The title clearly summarizes the main change: durable agent mounts.
Description check ✅ Passed The description is detailed and directly describes the agent-mount changes in API, runner, web, and docs.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/agent-mounts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@mmabrouk

Copy link
Copy Markdown
Member Author

Design PR: #5215. This implements slices 1-3 from docs/design/agent-workflows/projects/agent-mounts/plan.md.

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jul 12, 2026

Copy link
Copy Markdown

@mmabrouk Got it — I'll review the changes now, covering the API endpoints, runner lifecycle wiring, and web panel per slices 1-3 of the design doc.

✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@mmabrouk mmabrouk left a comment

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A few inline notes to make the review faster. The runner wiring is the substantive part; the API and web slices follow the patterns already next to them. Happy to walk through anything.

const signAgentMount =
deps.signAgentMountCredentials ?? signAgentMountCredentials;
const agentMountCreds: MountCredentials | null =
artifactId && runCred

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The gate is artifactId && runCred, on purpose not sessionForMount. A run with no session still gets its agent folder. A durable per-agent mount that only worked inside a session would miss the point. runCred is the invoke caller's credential, so the sign call authenticates as them and the server resolves RBAC.

if (agentMountDir) {
// Daytona fixes daemon env at sandbox-create, before the best-effort remote mount. Exposing
// the signed mount's stable path is safe; discovery files are seeded only after mount success.
piExtEnv[AGENT_MOUNT_ENV_VAR] = agentMountDir;

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This line is the fix for the one real bug the review caught. The Daytona daemon reads its environment from piExtEnv, frozen when the sandbox is created a few lines below, not from the local env object (only the local daemon reads that one). So the mount dir has to land in piExtEnv here, before buildSandboxProvider. The path is deterministic (<cwd>-agent), so setting it before the mount succeeds is safe: the README and symlink are still seeded only after a real mount, so a failed remote mount just leaves the agent an empty folder.

environment.deps.unmountStorage ?? unmountStorage
)(environment.mountedCwd, { log }).catch(() => false);
}
if (!parked && !plan.isDaytona && environment.agentMountedPath) {

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Two guards worth a look. !plan.isDaytona: on Daytona the folder is a remote path the sandbox reclaims on destroy, so a host fusermount here would be a no-op. !parked: a keep-warm pause must leave the mount alive, the same rule the durable cwd follows just above.

const createLink = deps.symlink ?? symlink;
const removeLink = deps.unlink ?? unlink;
const linkPath = join(cwd, AGENT_FILES_LINK_NAME);
let replaceExisting = false;

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why this is more than create-if-absent. geesefs makes a symlink fine while the mount is live, but does not persist it across a remount: it silently comes back as a 0-byte file. So "something exists here" is not enough to trust. We keep only a correct-target symlink and rebuild anything else, which hands the agent a working link on every run.

this same derivation byte-identically so they address the same mount.
"""
try:
canonical_artifact_id = UUID(str(artifact_id))

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sign and query both derive the slug from the artifact id, so if the two derivations ever differed by a byte, a real mount would read as absent in the UI. Parsing to a UUID and lowercasing keeps them identical. It also turns a non-UUID id into a clean 422 here, instead of a 500 later when the slug validator rejects it.

@mmabrouk mmabrouk marked this pull request as ready for review July 12, 2026 10:30
@dosubot dosubot Bot added size:XXL This PR changes 1000+ lines, ignoring generated files. enhancement New feature or request labels Jul 12, 2026

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

🧹 Nitpick comments (6)
web/oss/src/components/SessionInspector/api.ts (1)

49-57: 🔒 Security & Privacy | 🔵 Trivial | ⚡ Quick win

Missing Zod validation at the API boundary.

The coding guidelines require: "Keep Zod validation at the API boundary even when using Fern-generated types, because local schemas still detect backend drift. Use safeParseWithLogging from @agenta/entities/shared for boundary validation so structured errors are logged without crashing." No validation is performed on the response from /mounts/agents/query.

As per coding guidelines: "Keep Zod validation at the API boundary even when using Fern-generated types, because local schemas still detect backend drift. Use safeParseWithLogging from @agenta/entities/shared for boundary validation so structured errors are logged without crashing."

🛡️ Suggested validation addition
 export async function fetchAgentMount(artifactId: string, projectId?: string | null) {
     const res = await axios.post<Awaited<ReturnType<typeof fetchMounts>>>(
         `${getAgentaApiUrl()}/mounts/agents/query`,
         {artifact_id: artifactId},
         {params: {project_id: projectId}, _ignoreError: true} as Record<string, unknown>,
     )
-    return res.data.mounts?.[0] ?? null
+    const parsed = safeParseWithLogging(agentMountQueryResponseSchema, res.data)
+    return parsed?.mounts?.[0] ?? null
 }

Where agentMountQueryResponseSchema would be a Zod schema matching {count: number, mounts: Mount[]}.

Source: Coding guidelines

services/runner/src/engines/sandbox_agent/agent-mount.ts (1)

92-97: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Repeated error-formatting logic across the file.

The pattern String(err instanceof Error ? err.message : err).slice(0, N) appears seven times with only the truncation length varying. Extracting a small formatErr(err, max = 200) helper would remove the duplication and make truncation length consistent.

♻️ Proposed refactor
+function formatErr(err: unknown, max = 200): string {
+  return String(err instanceof Error ? err.message : err).slice(0, max);
+}

Then replace each occurrence, e.g.:

-      `sign failed artifact=${artifactId}: ${String(err instanceof Error ? err.message : err).slice(0, 160)}`,
+      `sign failed artifact=${artifactId}: ${formatErr(err, 160)}`,

Also applies to: 115-121, 147-151, 183-188, 195-199, 205-209, 233-236

services/runner/tests/unit/agent-mount.test.ts (2)

128-152: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Missing coverage for seedAgentReadme's non-EEXIST failure log.

Only the atomic-write and EEXIST-no-op paths are tested; the branch where writeFile throws something other than EEXIST (which should call log) has no test, unlike the symmetric coverage given to linkAgentFiles's error branches below.


154-292: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Missing coverage for linkAgentFiles's unlink-failure log branch.

Every other branch of linkAgentFiles (missing/valid/degraded/wrong-target/lstat-failure/EEXIST/symlink-failure) is tested, but the case where unlink throws a non-ENOENT error (lines 194-199 in agent-mount.ts, the "agent-files unlink failed" log) isn't exercised.

docs/design/agent-workflows/projects/agent-mounts/plan.md (1)

50-53: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Specify a language for the fenced code block.

The code block at line 50 has no language tag, which triggers markdownlint MD040.

📝 Proposed fix
-
+```text
 POST /mounts/agents/sign?artifact_id=<uuid>&name=default              permission: RUN_SESSIONS
 POST /mounts/agents/query   {"artifact_id": "<uuid>", "name": "default"}  permission: VIEW_SESSIONS

Source: Linters/SAST tools

services/runner/src/engines/sandbox_agent.ts (1)

1289-1333: 🚀 Performance & Scalability | 🔵 Trivial | ⚡ Quick win

Duplicate tunnel-discovery call per Daytona turn.

discoverTunnelEndpoint() is invoked here for the agent mount, and separately for the durable cwd remote mount a few lines earlier (~1240). Both mounts are signed against the same mounts_store (see MountsService.sign_mount_credentials), so their endpoints are effectively identical in practice — yet an unreachable store triggers two independent HTTP round-trips to the ngrok API on every single Daytona turn instead of one.

♻️ Suggested approach

Hoist the reachability/tunnel-endpoint resolution once (e.g., right before the durable-cwd remote-mount block) and reuse the resolved endpoint for the agent-mount block too, falling back to a fresh discovery only if the two stores' endpoints actually differ.


ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: d4e961ff-51d7-4fa2-b35f-5d4bd42d2c42

📥 Commits

Reviewing files that changed from the base of the PR and between 795bb4b and ebb9d79.

📒 Files selected for processing (25)
  • api/oss/src/apis/fastapi/mounts/models.py
  • api/oss/src/apis/fastapi/mounts/router.py
  • api/oss/src/core/mounts/interfaces.py
  • api/oss/src/core/mounts/service.py
  • api/oss/src/core/mounts/types.py
  • api/oss/src/dbs/postgres/mounts/dao.py
  • api/oss/tests/pytest/acceptance/mounts/test_agent_mounts_basics.py
  • api/oss/tests/pytest/unit/mounts/test_agent_mounts.py
  • docs/design/agent-workflows/projects/agent-mounts/open-issues.md
  • docs/design/agent-workflows/projects/agent-mounts/plan.md
  • docs/design/agent-workflows/projects/agent-mounts/research.md
  • docs/design/agent-workflows/projects/agent-mounts/status.md
  • services/runner/src/engines/sandbox_agent.ts
  • services/runner/src/engines/sandbox_agent/agent-mount.ts
  • services/runner/tests/unit/agent-mount.test.ts
  • web/oss/src/components/AgentChatSlice/AgentChatPanel.tsx
  • web/oss/src/components/AgentChatSlice/components/SessionRail.tsx
  • web/oss/src/components/SessionInspector/PanelSessionInspectorButton.tsx
  • web/oss/src/components/SessionInspector/SessionInspectorButton.tsx
  • web/oss/src/components/SessionInspector/SessionInspectorDrawer.tsx
  • web/oss/src/components/SessionInspector/api.test.ts
  • web/oss/src/components/SessionInspector/api.ts
  • web/oss/src/components/SessionInspector/store.ts
  • web/oss/src/components/SessionInspector/tabs/MountsTab.tsx
  • web/packages/agenta-entities/src/workflow/state/molecule.ts

Comment thread api/oss/src/apis/fastapi/mounts/router.py
Comment thread services/runner/src/engines/sandbox_agent/agent-mount.ts
Comment on lines +49 to +57
// Fern has no query_agent_mount yet; migrate after client regeneration.
export async function fetchAgentMount(artifactId: string, projectId?: string | null) {
const res = await axios.post<Awaited<ReturnType<typeof fetchMounts>>>(
`${getAgentaApiUrl()}/mounts/agents/query`,
{artifact_id: artifactId},
{params: {project_id: projectId}, _ignoreError: true} as Record<string, unknown>,
)
return res.data.mounts?.[0] ?? null
}

@coderabbitai coderabbitai Bot Jul 12, 2026

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🟠 Major | ⚡ Quick win

Raw axios usage violates the Fern-only API guideline.

The coding guidelines explicitly require: "All new frontend API code must use the Fern-generated client (getAgentaSdkClient({host: getAgentaApiUrl()})) instead of raw axios." While the comment documents that the Fern client lacks this endpoint, this still violates the guideline. Please track the migration explicitly (e.g., a linked issue) and ensure the Fern client is regenerated promptly.

As per coding guidelines: "All new frontend API code must use the Fern-generated client (getAgentaSdkClient({host: getAgentaApiUrl()})) instead of raw axios."

📝 Suggested tracking comment
-// Fern has no query_agent_mount yet; migrate after client regeneration.
+// TODO(migration): Fern has no query_agent_mount yet; migrate to getAgentaSdkClient
+// after client regeneration. Tracked in issue #<TBD>.

Source: Coding guidelines

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Deliberate for now: the Fern client has no agent-mount endpoint yet, which the code comment notes. Tracked in open-issues.md on design PR #5215 to migrate once the client is regenerated. Keeping the raw call (with _ignoreError: true so a failed fetch does not pop a global toast) until then.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@mmabrouk, understood — thanks for the context. Deferring the migration until the Fern client is regenerated makes sense, and having it tracked in open-issues.md alongside design PR #5215 is sufficient for now. The _ignoreError: true handling is a reasonable stopgap too. No further action needed on this from my side.

Comment thread web/oss/src/components/SessionInspector/tabs/MountsTab.tsx Outdated
@mmabrouk mmabrouk force-pushed the feat/agent-mounts branch from ebb9d79 to 217d141 Compare July 12, 2026 10:50
@mmabrouk

Copy link
Copy Markdown
Member Author

Live QA on Daytona: passed

Ran an agent workflow twice on Daytona (eu.preview key dtn_31...b8b, EU target) and checked the mount from inside the live sandbox.

Check Result
${cwd}-agent is a live mount pass
README.md seeded with the expected copy pass
agent-files symlink resolves in the cwd pass
AGENTA_AGENT_MOUNT_DIR reaches the Daytona daemon pass
Persistence: marker written in run 1 read back in run 2, README not rewritten pass

The env-var check is the one the review flagged. It passed two ways: the var is set in the live sandbox's environment, and Daytona's own stored sandbox record echoes it, which proves it rode daytonaEnvVars(piExtEnv, secrets) into the sandbox at create time.

Unrelated infra bug found along the way (not this PR)

The EE dev runner boots Daytona sandboxes from the wrong snapshot: its compose service reads plain DAYTONA_SNAPSHOT (set to daytona-small, which has no sandbox-agent daemon) instead of SANDBOX_AGENT_DAYTONA_SNAPSHOT (agenta-sandbox-pi). Any Daytona run on agenta-ee-dev-wp-b2-rendering-runner-1 hangs on daemon-health polling. This blocks all Daytona runs on that container, not just agent mounts. The QA ran against a sibling container with the correct snapshot. Worth a separate fix.

mmabrouk added a commit that referenced this pull request Jul 12, 2026
Self-healing discovery symlink (geesefs drops symlinks across remount), the
Daytona env-var delivery fix, live QA results (incl. Daytona verified), and a
new open-issues log. Implementation is PR #5247.

Claude-Session: https://claude.ai/code/session_01UogudBC9VRCRkpT5bZVxex
@mmabrouk mmabrouk force-pushed the feat/agent-mounts branch from 217d141 to 0255e0c Compare July 12, 2026 10:57
@dosubot dosubot Bot added size:XL This PR changes 500-999 lines, ignoring generated files. and removed size:XXL This PR changes 1000+ lines, ignoring generated files. labels Jul 12, 2026
@mmabrouk mmabrouk force-pushed the feat/agent-mounts branch from 0255e0c to fc0e857 Compare July 12, 2026 11:00
@mmabrouk mmabrouk added the needs-review Agent updated; awaiting Mahmoud's review label Jul 12, 2026
mmabrouk added 2 commits July 13, 2026 01:31
…egment

On a natural "remember this for next time" prompt, an agent had no way to
know its cwd is throwaway and agent-files/ is durable, so it wrote to its
own session-scoped memory and the note was lost next session.

Append a short platform guidance segment to the harness's SYSTEM PROMPT
(never the author's CLAUDE.md/AGENTS.md) whenever an agent mount exists
for the run:
- Pi: combined into plan.appendSystemPrompt, rendered via the existing
  APPEND_SYSTEM.md file channel.
- Claude: passed as the ACP session's _meta.systemPrompt.append, which
  claude-agent-acp forwards into the claude-agent-sdk's
  {type:"preset",preset:"claude_code",append} option - additive to the
  default system prompt, so CLAUDE.md loading is unaffected.

Verified live: a fresh session writes the note into agent-files/notes.md
(confirmed durable via the mounts API after the FUSE cache clears), and a
cold new session correctly recalls it by checking agent-files/ first.
The session mount lives inside the agent's durable folder (the runner
symlinks it in as agent-files/), so present it that way in the Session
Inspector's Mounts tab: the agent mount is the parent Collapse panel, and
the session mount is a nested section inside it, instead of two sibling
sections. Falls back to the flat session-only view when there is no
artifact/agent mount to nest under.

Presentation only - the runtime mount layout is unchanged, and both
mounts keep their own file-listing queries.
@CLAassistant

Copy link
Copy Markdown

CLA assistant check
Thank you for your submission! We really appreciate it. Like many open source projects, we ask that you all sign our Contributor License Agreement before we can accept your contribution.
1 out of 2 committers have signed the CLA.

✅ mmabrouk
❌ QA Bot


QA Bot seems not to be a GitHub user. You need a GitHub account to be able to sign the CLA. If you have already a GitHub account, please add the email address used for this commit to your account.
You have signed the CLA already but the status is still pending? Let us recheck it.

@mmabrouk

Copy link
Copy Markdown
Member Author

🤖 The AI agent says: Superseded by #5268, which stacks the same Agent Mounts feature directly on #5204 (Mount File Viewer) and excludes the deferred Daytona Secret Delivery work.

@mmabrouk mmabrouk closed this Jul 13, 2026
@mmabrouk mmabrouk deleted the feat/agent-mounts branch July 13, 2026 02:27
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request needs-review Agent updated; awaiting Mahmoud's review size:XL This PR changes 500-999 lines, ignoring generated files.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants