Agentic Work
TensorSharp.AgentHost turns a local model response into a bounded generate → inspect → run → repair → answer loop. It can load Agent Skills only when they are needed, work with files through five built-in code tools, run checks inside an OS sandbox, and return finished artifacts to an ordinary CLI, Web UI, Ollama, or OpenAI client.
What happens in one turn
Advertise capabilities cheaply
TensorSharp puts only each reachable skill's name and description in the prompt. A selected skill is preferred and made reachable; selection does not load its instructions.
Load instructions on demand
When a tool-capable model decides a skill applies, it calls
skills_read(skill, "SKILL.md"). TensorSharp answers that built-in call in process, appends the result to the same conversation, and generates again.Do the work
If the operator enabled
--code-exec, the model can inspect, edit, create, patch, and test files in a request or chat workspace. Skill scripts can participate too, but only under their separate execution opt-in.Return an ordinary answer
The client receives the final completion rather than TensorSharp's internal tool calls. A caller's own tools are never executed by TensorSharp; those calls are returned normally for the caller to service.
Current scope is one model and one in-process tool loop. TensorSharp does not spawn multiple agents or delegate to subagents, and it has no interactive approval queue before each command. The operator grants or withholds skills, script execution, code execution, networking, and installation when the CLI or server starts; an internal tool then runs under that policy or refuses.
TensorAgent on iPhone and iPad
TensorAgent packages this local chat and agent experience as a .NET MAUI iOS/iPadOS app. The engine is statically linked as an iOS .xcframework and uses ggml_metal on physical devices. The app shares the host-neutral chat pipeline (TensorSharp.Chat) with the CLI and server, but serves the UI through an in-process loopback server because iOS has no ASP.NET Core runtime pack. What it shares is the API, not the document: the phone gets its own page, laid out for a thumb rather than a mouse and a wide window, bound to the same WebUiChatService and SkillsService routes.
Models download to the device with resume and SHA-256 verification; conversations survive app restarts; photos, camera, video, files, and dictation use the same chat attachment flow. Since iOS cannot start child processes, TensorAgent's shell, Python, and JavaScript integrations are runtime-backed and report their limits explicitly.
Three constraints a phone imposes that a desktop does not, and what the app does about each:
| Constraint | What the app does |
|---|---|
| The screen goes away mid-answer | Generation belongs to a host-side manager, not to the WebView — WebKit suspends a page whose view left the window. A turn continues while the app is backgrounded and the page re-attaches to it on return. |
| The first message of a launch pays for the whole shared prefix | The state at the end of the prompt every chat shares is checkpointed to disk per model and restored at admission on the next launch. Measured on iPhone 17 Pro Max with Qwen3.5 9B: a 54 s cold first message becomes a 1.2 s warm-up and a ~0.6 s first message. |
| iOS jetsam kills without a stack or a message | The engine's memory policy is sized against what jetsam charges — wired file pages count against the device, not the process — so caches start small and grow, a request reserves a bounded reply, and a memory warning releases what only serves the next request's speed. |
See the TensorAgent README for build, simulator, device, and verification instructions.
Agent Skills and progressive disclosure
A skill is a directory containing a required SKILL.md plus any scripts, references, and assets its instructions need. TensorSharp follows the Agent Skills specification and can load published skill repositories without rewriting their layout.
pdf/
├── SKILL.md
├── scripts/
│ └── extract_tables.py
├── references/
│ └── forms.md
└── assets/
└── template.pdf
| Tier | What reaches the model | When |
|---|---|---|
| Metadata | name + description | Up front for selected and discoverable skills. This is the normal prompt cost. |
| Instructions | The complete SKILL.md body | Only after the model activates it with skills_read(skill, "SKILL.md"). |
| Bundled files | References, scripts, and assets | Paths and sizes are indexed; content is fetched only through skills_read, paged at 48 KB. |
The normal metadata block uses about 2% of the context window, clamped to roughly 1,024–10,000 tokens. The old behavior that inlined every selected body is off by default. A model family that cannot carry and parse tool declarations has no opportunity to call skills_read, so TensorSharp instead inlines selected skill bodies within a bounded fallback budget and does not advertise skills it cannot load.
The built-in skill tools are skills_list and skills_read. A third tool, skills_run, appears only with --skills-allow-exec. Script execution is off by default and remains separate from model-authored code execution.
Five code tools, off by default
--code-exec adds a patch-first coding surface. It does not require a skill, and merely installing or selecting a skill does not turn it on.
| Tool | Purpose | Important bound |
|---|---|---|
read_file | Read real workspace bytes with line numbers. | Bounded output; paths stay inside the workspace. |
edit_file | Replace one exact string in one file. | Refuses missing or ambiguous matches instead of guessing. |
write_file | Create a file or deliberately replace it whole. | Confined to the workspace and reports rewrite scope. |
apply_patch | Create, update, rename, or delete several files by anchored hunks. | Atomic: all hunks apply or none do. |
shell | Run tests, commands, and generated programs. | Timeout and output caps; sandbox-or-refuse on supported hosts. |
Failures are designed to be repairable. A diagnostic that identifies a regular source file in the workspace returns a bounded source excerpt and asks for the smallest exact edit before rerunning. Syntax checks, missing-import installation when allowed, and installed-library API hints reduce avoidable model rounds.
Workspace lifetime and artifacts
| Surface | Workspace lifetime | Result |
|---|---|---|
| CLI / interactive chat | Persistent for the process or chat session. | One tool call can inspect or repair files written by an earlier call. |
| Bundled Web UI | Persistent for that Web chat session. | Live writing/running activity, bounded output, and generated-file download chips. |
| OpenAI and Ollama HTTP chat | One private workspace across every internal tool round in that request; deleted when the response finishes. | Repairs work within a request, while separate requests remain stateless. |
skills_run without code execution | Fresh per-call scratch directory, deleted on return. | Scripts do not accidentally share state. |
When code execution is enabled, a skill script shares the same request/chat workspace as the five code tools. The server copies produced files into its bounded artifact store; the Web UI exposes safe download links, and artifact responses are sent as attachments with content sniffing disabled.
Sandbox-or-refuse security model
Enabling execution is an operator decision, not a request field. On macOS and Linux, model-authored commands and opted-in skill scripts run with a workspace write boundary, an unreadable home directory, a scrubbed environment, and network denied by default. Paths are also checked in process before the OS sandbox is entered.
| Platform | Model-authored code | Skill scripts |
|---|---|---|
| macOS | sandbox-exec; refuses if confinement is unavailable. | Default --skills-sandbox required; sandboxed or refused. |
| Linux | Requires bwrap 0.12.0 or newer; otherwise refuses. | The same safe bwrap requirement under the default required mode. |
| Windows | No filesystem/network sandbox is implemented. --code-exec-unconfined is required and intentionally grants the process's file and network reach. | A job object bounds the process tree but not files or sockets. Default required refuses; --skills-sandbox preferred is the explicit weaker opt-in. |
Sandboxed does not mean a disposable VM. macOS deliberately retains shared /private/tmp compatibility and some local Unix IPC. Seatbelt restrictions pass to children and ordinary process groups are stopped, but a deliberately detached child can outlive the request; every tool result reports that gap. Linux shares parts of the host network namespace even while IP networking is denied. Every reported platform gap matters. Do not enable unconfined execution on a server other people can reach.
Network and package installation are separate opt-ins
| Option | Default | What it grants |
|---|---|---|
--code-exec-allow-network | Off | Unrestricted host IP networking for every model-authored command, including LAN/loopback services. It is not domain-filtered. |
--skills-allow-network | Off | Network for opted-in bundled skill scripts only. It does not affect shell. |
--code-exec-allow-install | Off | Host-performed recognized pip/npm installs. The host reads and validates package names, uses prebuilt wheels / disabled npm scripts, and removes the install from the model's command. |
--code-exec-packages, --code-exec-install-index, and --code-exec-install-domains constrain the host installer. They are not a security boundary after unrestricted command networking is enabled, because generated code can download an artifact directly. The script and code network switches are intentionally independent.
Start an agentic session
Skills can be used without executing code:
dotnet run --project TensorSharp.Cli -c Release -- \
--model models/model.gguf --backend ggml_metal \
--skills-dir ./skills --skill pdf -i
Add the five code tools explicitly. This example keeps both command networking and package installation off:
dotnet run --project TensorSharp.Cli -c Release -- \
--model models/model.gguf --backend ggml_metal \
--skills-dir ./skills --skill pdf --code-exec -i
Or host the same loop for the Web UI and compatible APIs:
dotnet run --project TensorSharp.Server.Host -c Release -- \
--model models/model.gguf --backend ggml_cuda \
--skills-dir ./skills --code-exec
# Browser UI: http://localhost:5000/
# Liveness: http://localhost:5000/health
On Linux, install bwrap 0.12.0 or newer first. On Windows, code execution remains unavailable unless the operator deliberately adds --code-exec-unconfined. Do not copy that Windows escape hatch onto a shared server.
Use skills from an ordinary HTTP client
The server must be started with a skills registry. A client then selects skills on the normal chat request; TensorSharp services its own skill/code calls internally and returns the completed response.
{
"model": "local",
"messages": [
{ "role": "user", "content": "Extract the tables and summarize the findings." }
],
"skills": ["pdf"],
"skills_discovery": false,
"stream": true
}
skills_discovery defaults to true. Code execution has no per-request enable field: the operator either started the host with --code-exec or the five tools are absent. The agent loop allows 8 internal rounds by default, or 24 when code execution is enabled; --skills-max-rounds sets an explicit 1–64 bound.
Model-family limits
The in-process loop requires a model family that can both carry tool declarations and parse tool calls. Families without that round trip receive selected skill instructions inline as a fallback, but are not offered the five code tools. TensorSharp logs the limitation rather than silently pretending code execution is available.