Why did my skill's output never reach the user?
Cowork runs in two different sandboxes, and they disagree about what 'delivering a file' means. In one, writing the file to the workspace location is itself the delivery. In the other, the location delivers nothing and the file only reaches the user if your skill presents it — and the session filesystem is discarded when the session ends, so an undelivered file is simply lost. Write the deliverable to a stated path, then present it to the user, and say the path either way.
Do not rely on writing a file to a directory as the act of delivering it.
In one sandbox the workspace location is itself the delivery channel and writing there is enough. In the other it delivers nothing: the file exists only inside a session filesystem that is discarded when the session ends, and the user never sees it. A skill written against the first behaviour loses its deliverables silently in the second — no error, no warning, just nothing.
Write the deliverable to a stated path, then present it to the user. If you cannot present it, say where it is.
Producing the file and delivering it are two separate acts. Instruct your skill to present the finished file to the user rather than naming a specific tool to do it with: which delivery mechanism exists depends on the sandbox and on the product your skill is running in, and a skill that names one tool breaks everywhere that tool is absent. Always state the path as well, so the work is recoverable if presentation is unavailable.
State the path of what you produced even when a delivery tool worked.
It costs one sentence and it is the only part of your output that survives every sandbox, every tool-surface change, and a user reading a transcript later. If delivery silently failed, the stated path is the difference between a recoverable result and a lost one.
Never call a tool that writes to the user's own disk from inside a skill.
Committing files onto the user's machine is infrastructure the agent and the user negotiate through folder-connection consent, not something a skill should reach for. No first-party skill names such a tool. Present the file and let the delivery path be decided outside your skill.
Do not deliver by starting a local server and opening it. Write a self-contained file and present that.
A skill that generates something to look at — a report, a viewer, a chart — is tempted to serve it and open a browser at the address. That does not work here, and the reason survives whichever surfaces happen to be available: anything your shell starts runs where the shell runs, which is not where a viewer would be looking, and a plain file path on disk is not reachable that way either. Write one self-contained file with everything inlined, put it where deliverables belong, and present it. If the surface offers a way to publish a rendered page, that is the better target than a server. Build the interaction so it does not need a round trip back to your skill: a page that asks the person to copy something back into the conversation works, a page that expects to POST its results somewhere does not.
Do not hand-copy files into the outputs location before presenting them.
The tool that presents files to the user copies what you give it out of the scratch space for you. Copying first and then presenting the copy produces duplicates and obscures which file is the deliverable.
Assume the session filesystem does not outlive the session.
In the remote sandbox the filesystem is discarded at session end. In the local sandbox undelivered files are hidden from the user rather than destroyed, and can in principle be surfaced on a later turn — but a skill should not depend on that difference. Deliver within the turn that produces the file.
Assume your skill may run in either sandbox, and never assume which one.
A Cowork session runs in one of two sandboxes: a local one on the user's own machine, or a remote one on Anthropic's infrastructure. They differ in where the filesystem lives, whether it survives the session, and how a file reaches the user. Which one a session gets depends on how it was created and on account configuration, neither of which a skill can control or reliably detect — so write for both.
Nothing will report progress for you — if a long run should say anything mid-way, your skill has to say it.
Plain text you write between tool calls does reach the user here, and the shipped instructions do ask for it in general terms: say what you are about to do before the first tool call, and give brief updates when you find something load-bearing or change direction. But the wording of that instruction is chosen per model by the service rather than by anything on the machine, so you cannot count on a particular version of it, and in a long tool loop it measurably does not hold. Measured across every session on one machine, work in this environment produces user-visible text on roughly a third of its turns, against about half in the terminal, and silent runs of well over a hundred consecutive turns occur. There is a mechanism meant to catch exactly that — a nudge injected once a run has gone several turns without saying anything — and across every stretch long enough to qualify, it never fired once. Treat it as absent. If a phase boundary, a decision, or a surprise is worth the user knowing about while the work is still running, write the instruction to say so into the skill itself, at the step where it matters. A useful shape to copy: acknowledge in one line, work, then report — and between those, only when something happened that carries information, not as filler.
What is not established
- Which sandbox a given session runs in is decided by the surface it was created from and by account-level configuration. There is no reliable way for a skill to force one, and no documented way to read it directly — check for the tools you need instead.