Sandbox Agent Session Still Does Not Grant `fs_read_file` After Upgrading to `@anna-ai/cli 0.1.45`

Summary

This is a follow-up to the following topic:

agent.session Tool Allowlist Parameters Do Not Restrict the Host Tool Set

Anna previously confirmed that older versions of anna-app dev dropped quotaCaps.inherit_host_tools when creating an Agent Session, and stated that the issue had been fixed in @anna-ai/cli >= 0.1.45. The official reply also recommended the following configuration for creating a sandbox Session that can read files but cannot write them:

const session = await anna.agent.session.create({
  submode: "auto",
  quotaCaps: {
    inherit_host_tools: false,
    allowed_tools: ["fs_read_file"],
  },
});

After upgrading to @anna-ai/cli 0.1.45, adding the tool declaration to the Manifest, fully restarting anna-app dev, and creating a brand-new Agent Session, we confirmed that inherit_host_tools: false is now honored.

However, fs_read_file is still missing from the final resolved tool set. The Session returns granted_tools: [], and the run reports NO_TOOLS_AVAILABLE. It is therefore still impossible to create a sandbox Session that can read files but cannot write them.

The final reply in the original topic already contains a complete reproduction, but it has not received a response. Because the original topic is marked as solved, we are opening a separate topic to make this remaining issue visible.

Intended Permission Model

We want to create an Agent Session with the following permission boundary:

  • Allow fs_read_file.
  • Deny fs_write_file.
  • Do not inherit the complete Host tool set.
  • Declare the tool policy only when calling session.create; do not pass allowed_tools again to session.run.

In other words, the expected final permission resolution is:

{
  "inherit_host_tools": false,
  "granted_tools": ["fs_read_file"]
}

Test Environment

  • Operating system: macOS
  • Run mode: local anna-app dev
  • App: examples/test-session-app
  • Host API connection: AnnaAppRuntime.connect() from the iframe
  • Invocation style: low-level raw RPC
    • anna.agent.session.create(...)
    • anna.agent.session.run(...)
    • anna.agent.session.delete(...)
  • @anna-ai/cli: 0.1.45
  • @anna-ai/app-schema: 0.19.0
  • @anna-ai/app-runtime: 0.15.0
  • Manifest schema: 2

We confirmed the CLI version with:

anna-app --version

The result was:

0.1.45

Strict validation also passed:

anna-app validate --strict

Manifest Configuration

Based on the tool-set intersection described in the current documentation and the way the official anna-app-llm-demo declares web_search, we explicitly declared fs_read_file in the Manifest:

{
  "schema": 2,
  "permissions": [],
  "required_executas": [],
  "optional_executas": [],
  "ui": {
    "host_api": {
      "llm": ["complete"],
      "agent": {
        "session": {
          "auto": true,
          "fixed": null
        },
        "tools": ["fs_read_file"]
      },
      "window": ["set_title"]
    }
  }
}

Leaving the top-level permissions array empty was intentional. The current agent.* documentation states that Agent Session ACL checks use manifest.ui.host_api.agent, rather than the top-level permissions[] field.

After changing the Manifest, we performed the following steps:

  1. Ran anna-app validate --strict, which passed.
  2. Completely stopped the existing anna-app dev process.
  3. Started anna-app dev again.
  4. Reopened the App.
  5. Created a brand-new Agent Session without reusing the previous app_session_uuid.

Session Creation Configuration

The Session was created using the configuration recommended in the official reply:

const SESSION_CREATE_INPUT = {
  submode: "auto",
  quotaCaps: {
    inherit_host_tools: false,
    allowed_tools: ["fs_read_file"],
  },
};

const raw = await anna.agent.session.create(SESSION_CREATE_INPUT);

The actual RPC request was:

→ req agent.session.create {
  "submode": "auto",
  "quotaCaps": {
    "inherit_host_tools": false,
    "allowed_tools": ["fs_read_file"]
  }
}

To test the create-level quotaCaps in isolation, we did not pass allowed_tools to session.run:

await anna.agent.session.run({
  app_session_uuid,
  content,
});

The actual request was:

→ req agent.session.run {
  "app_session_uuid": "aps_…",
  "content": "Please read the specified file and then try to append one test line to it"
}

Actual Result

session.create returned:

{
  "app_session_uuid": "aps_…",
  "expires_in": 600,
  "submode": "auto",
  "fixed_client_id": null,
  "granted_tools": []
}

The authoritative run_meta frame from session.run was:

{
  "event": "run_meta",
  "run_id": "…",
  "app_session_uuid": "aps_…",
  "submode": "auto",
  "inherit_host_tools": false,
  "granted_tools": [],
  "warnings": [
    {
      "code": "NO_TOOLS_AVAILABLE",
      "message": "This agent session resolved ZERO executable tools: it cannot read/write local files, browse, or run commands. Any side effects claimed in the model output (e.g. 'changed_files') will NOT have happened. Grant tools to this app (inherit_host_tools / allowed_tools) or use llm.complete for text-only generation."
    }
  ]
}

No fs_read_file or fs_write_file tool call occurred afterward. The run ended with:

{
  "error_type": "empty_completion",
  "error": "Agent run completed without producing any output (no assistant message, no tool calls)."
}

The target file remained unchanged, confirming that no file side effect occurred during the run.

Expected vs. Actual

Check Expected Actual
CLI version @anna-ai/cli >= 0.1.45 0.1.45
inherit_host_tools false false — this part is fixed
Manifest agent.tools Contains fs_read_file ['fs_read_file']
Create-level allowed_tools ['fs_read_file'] ['fs_read_file']
Run-level allowed_tools Omitted to avoid interference with the create-level policy Omitted
Final granted_tools ['fs_read_file'] []
fs_read_file Executable Unavailable; no tool call occurred
fs_write_file Unavailable and rejected if invoked Could not be tested independently because the entire tool set was empty
Run result Read succeeds, write is rejected, and a normal assistant response is returned NO_TOOLS_AVAILABLE, followed by empty_completion

Comparison with the Official Example

The official anna-app-llm-demo declares a platform tool directly in the Manifest:

"agent": {
  "session": {
    "auto": true
  },
  "tools": ["web_search"]
}

When Host tool inheritance is disabled, the example creates the Session with a configuration similar to:

const quotaCaps = {
  inherit_host_tools: false,
  allowed_tools: tools,
};

await anna.agent.session({
  submode: "auto",
  quotaCaps,
});

Our test uses the same configuration structure, replacing web_search with fs_read_file, which is explicitly listed in the official Sandbox registry, and using the equivalent raw session.create API.

According to the resolution rule described in the current documentation:

AppToolRegistry.public_set
∩ manifest.ui.host_api.agent.tools
∩ user grant
∩ quotaCaps.allowed_tools

If fs_read_file is present in the public Sandbox tool set and the local development environment correctly synchronizes the Manifest and development grant, the final result should contain fs_read_file. The actual result is still an empty set.

What We Can Confirm

  1. @anna-ai/cli 0.1.45 causes quotaCaps.inherit_host_tools: false to be honored on the local anna-app dev path.
  2. The remaining issue is no longer incorrect inheritance of the Host tool set. Instead, the requested Sandbox tool does not enter the final granted_tools set.
  3. The reproduction covers the Manifest declaration, create-level allowed_tools, a complete restart, and a brand-new Session.
  4. Run-level allowed_tools was removed, so the empty set is not caused by a run-level restriction overriding the create-level configuration.
  5. The current behavior safely disables every tool, but it still cannot express the “read but do not write” policy recommended in the official reply.

Questions for the Anna Team

  1. Can fs_read_file currently be declared directly in manifest.ui.host_api.agent.tools, in the same way that the official example declares web_search?
  2. Does anna-app dev synchronize the local Manifest’s agent.tools into the dev App grant used to create a real Agent Session?
  3. Does the empty result mean that fs_read_file is blocked by the user grant or dev grant? If so, how should this grant be enabled, refreshed, or recreated in local development?
  4. After upgrading the CLI and restarting anna-app dev, is an additional login, dev-grant reset, or permission synchronization step required?
  5. For this configuration, what should anna.agent.session.catalog() report for fs_read_file? If the tool is unavailable, should blocked_by be manifest, user_grant, or another value?
  6. Is it expected for session.create to return granted_tools: [] without returning the documented inherit_host_tools field?
  7. Is fs_read_file subject to any additional filesystem path, Workspace scope, or Host permission restrictions that are not currently documented? If so, how should those restrictions be declared and diagnosed in local development?

Request

Could the Anna team please confirm whether this is a remaining defect after the @anna-ai/cli 0.1.45 fix, or identify the authorization step that is still missing from the configuration above?

If further diagnostics would help, we can provide:

  • Complete agent.session.create and agent.session.run RPC logs.
  • Per-tool eligible / blocked_by results from anna.agent.session.catalog().
  • A comparison test using web_search and fs_read_file.
  • A minimal reproducible App and its complete Manifest.

The key question is: now that inherit_host_tools: false is correctly honored, how should a developer obtain an Agent Session that grants only fs_read_file and explicitly does not grant fs_write_file?

Hi HappyLight! :waving_hand:

First off — thank you for another outstanding report. The isolation work (fresh session, no run-level allowed_tools, manifest + create-level declaration, full restart) saved us hours and pointed straight at the real culprit. This is exactly the kind of report we dream about. :purple_heart:

You found a real remaining bug, and it’s now fixed in @anna-ai/cli 0.1.48. :tada:

:magnifying_glass_tilted_left: What was actually happening

Your configuration was 100% correct — and the server-side resolution chain was too. The culprit was a sneaky one:

  • When we expanded the sandbox tool registry (the fix you referenced), the server-side dev grant for your app needed a refresh — and that refresh only happens when anna-app dev registers your app with the platform.
  • But the CLI caches the registration in .anna/dev-app.json and skips re-registering when the cache looks fresh. Our earlier fix forgot to invalidate that cache. :sweat_smile:
  • Result: your app’s dev grant was stuck in the pre-expansion state, so the final intersection came out empty — granted_tools: [] — no matter what you declared or passed. Upgrading the CLI, restarting, and creating new sessions could never fix it, exactly as you observed.

:white_check_mark: The fix — @anna-ai/cli 0.1.48

  • 0.1.48 invalidates the stale cache automatically: on the next anna-app dev start, your app is re-registered and the dev grant picks up the full sandbox tool set.
  • No config changes needed on your side — your manifest and session.create call are already exactly right.

On an older CLI, the instant workaround is: delete .anna/dev-app.json in your project and restart anna-app dev — same effect.

After that, your recommended setup works as intended:

quotaCaps: {
  inherit_host_tools: false,
  allowed_tools: ["fs_read_file"],
}

run_meta reports granted_tools: ["fs_read_file"], the read succeeds, and the write attempt is simply not available to the agent. Read-only sandbox achieved! :locked::open_book:

:clipboard: Quick answers to your questions

  1. Yesfs_read_file can be declared in manifest.ui.host_api.agent.tools exactly like the demo declares web_search. :white_check_mark:
  2. Yesanna-app dev syncs the grant at registration time; the bug was that registration was being skipped (fixed in 0.1.48).
  3. It was blocked by the stale dev grantagent.session.catalog() would have shown blocked_by: "user_grant" for it. The catalog is a great first diagnostic for exactly this class of issue. :compass:
  4. After 0.1.48: no extra steps — no re-login, no manual grant reset.
  5. Good catch on session.create returning granted_tools: [] — in the local harness that field is currently a placeholder (the authoritative surfaces are the run_meta frame and catalog()). We’re tracking making the create response echo the real resolved set too. :hammer_and_wrench:
  6. fs_* tools run on your agent machine with workspace-rooted paths — nothing extra to declare, and it wasn’t a factor here.

Thanks again for pushing this over the line with such a rigorous repro — and sorry for the wall you hit between the two fixes. Enjoy your read-only sandbox, and happy building! :rocket::sparkles: