[{"data":1,"prerenderedAt":554},["ShallowReactive",2],{"navigation_docs":3,"-attachments":139,"-attachments-surround":549},[4,8,12,16,20,24,28,32,59,63,117,121,135],{"title":5,"path":6,"stem":7},"Introduction","\u002Fintroduction","1.introduction",{"title":9,"path":10,"stem":11},"Memory","\u002Fmemory","10.memory",{"title":13,"path":14,"stem":15},"Customization","\u002Fcustomization","11.customization",{"title":17,"path":18,"stem":19},"Deployment","\u002Fdeployment","12.deployment",{"title":21,"path":22,"stem":23},"Reference","\u002Freference","13.reference",{"title":25,"path":26,"stem":27},"Quickstart","\u002Fquickstart","2.quickstart",{"title":29,"path":30,"stem":31},"Installation","\u002Finstallation","3.installation",{"title":33,"icon":34,"path":35,"stem":36,"children":37,"page":58},"Connectors","i-lucide-cable","\u002Fconnectors","4.connectors",[38,42,46,50,54],{"title":39,"path":40,"stem":41},"Telegram","\u002Fconnectors\u002Ftelegram","4.connectors\u002F1.telegram",{"title":43,"path":44,"stem":45},"Slack","\u002Fconnectors\u002Fslack","4.connectors\u002F2.slack",{"title":47,"path":48,"stem":49},"Email","\u002Fconnectors\u002Femail","4.connectors\u002F3.email",{"title":51,"path":52,"stem":53},"API","\u002Fconnectors\u002Fapi","4.connectors\u002F4.api",{"title":55,"path":56,"stem":57},"Terminal","\u002Fconnectors\u002Fterminal","4.connectors\u002F5.terminal",false,{"title":60,"path":61,"stem":62},"Attachments","\u002Fattachments","5.attachments",{"title":64,"icon":65,"path":66,"stem":67,"children":68,"page":58},"Tools","i-lucide-wrench","\u002Ftools","6.tools",[69,73,77,81,85,89,93,97,101,105,109,113],{"title":70,"path":71,"stem":72},"File Manager","\u002Ftools\u002Ffile-manager","6.tools\u002F1.file-manager",{"title":74,"path":75,"stem":76},"Text-to-Speech","\u002Ftools\u002Ftext-to-speech","6.tools\u002F10.text-to-speech",{"title":78,"path":79,"stem":80},"Confirmations","\u002Ftools\u002Fconfirmations","6.tools\u002F11.confirmations",{"title":82,"path":83,"stem":84},"Adding Custom Tools","\u002Ftools\u002Fadding-custom-tools","6.tools\u002F12.adding-custom-tools",{"title":86,"path":87,"stem":88},"Image Manager","\u002Ftools\u002Fimage-manager","6.tools\u002F2.image-manager",{"title":90,"path":91,"stem":92},"Web Request","\u002Ftools\u002Fweb-request","6.tools\u002F3.web-request",{"title":94,"path":95,"stem":96},"Reminders","\u002Ftools\u002Freminders","6.tools\u002F4.reminders",{"title":98,"path":99,"stem":100},"Heartbeats","\u002Ftools\u002Fheartbeats","6.tools\u002F5.heartbeats",{"title":102,"path":103,"stem":104},"Email Manager","\u002Ftools\u002Femail-manager","6.tools\u002F6.email-manager",{"title":106,"path":107,"stem":108},"Calendar","\u002Ftools\u002Fcalendar","6.tools\u002F7.calendar",{"title":110,"path":111,"stem":112},"Read Database","\u002Ftools\u002Fread-database","6.tools\u002F8.read-database",{"title":114,"path":115,"stem":116},"Headless Browser","\u002Ftools\u002Fbrowser","6.tools\u002F9.browser",{"title":118,"path":119,"stem":120},"Tinker","\u002Ftinker","7.tinker",{"title":122,"icon":123,"path":124,"stem":125,"children":126,"page":58},"Skills","i-lucide-sparkles","\u002Fskills","8.skills",[127,131],{"title":128,"path":129,"stem":130},"Overview","\u002Fskills\u002Foverview","8.skills\u002F1.overview",{"title":132,"path":133,"stem":134},"Adding Custom Skills","\u002Fskills\u002Fadding-custom-skills","8.skills\u002F2.adding-custom-skills",{"title":136,"path":137,"stem":138},"Personas","\u002Fpersonas","9.personas",{"id":140,"title":60,"body":141,"description":542,"extension":543,"links":544,"meta":545,"navigation":546,"path":61,"seo":547,"stem":62,"__hash__":548},"docs\u002F5.attachments.md",{"type":142,"value":143,"toc":532},"minimark",[144,148,161,169,172,176,182,190,194,210,225,242,246,249,257,279,282,311,323,327,330,336,347,354,419,423,449,455,465,469,490,499,503,525,528],[145,146,5],"h2",{"id":147},"introduction",[149,150,151,152,156,157,160],"p",{},"Files in Laraclaw flow in two directions: ",[153,154,155],"em",{},"into"," the agent (a photo you sent on Telegram, a PDF emailed to the bot, a voice note in Slack) and ",[153,158,159],{},"out"," of the agent (a chart it generated, a TTS reply, a report it wrote).",[149,162,163,164,168],{},"We need a single, predictable place to keep both, and we need it to be safe — the agent's tools shouldn't be able to scribble all over the message-attachment lifecycle. That's what the ",[165,166,167],"strong",{},"attachments disk"," is for.",[149,170,171],{},"Let's break down how it works.",[145,173,175],{"id":174},"the-big-picture","The Big Picture",[149,177,178,179,181],{},"Every file that flows through Laraclaw lives on a single Laravel disk: the ",[165,180,167],{},". Inbound files land in one folder, outbound files land in another. Both folders are scoped by the UUID of the message that triggered them.",[149,183,184,185,189],{},"There is no ",[186,187,188],"code",{},"laraclaw_attachments"," table. The filesystem is the source of truth, and the message UUID is the primary key.",[145,191,193],{"id":192},"the-attachments-disk","The Attachments Disk",[195,196,201],"pre",{"className":197,"code":198,"language":199,"meta":200,"style":200},"language-env shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","LARACLAW_ATTACHMENTS_DISK=local\n","env","",[186,202,203],{"__ignoreMap":200},[204,205,208],"span",{"class":206,"line":207},"line",1,[204,209,198],{},[149,211,212,213,216,217,220,221,224],{},"This is a Laravel filesystem disk name from ",[186,214,215],{},"config\u002Ffilesystems.php",". The default is ",[186,218,219],{},"local",". Point it at S3, an NFS mount, or whatever you need — the only requirement is that every process touching Laraclaw (web nodes, queue workers, the ",[186,222,223],{},"imap:watch"," listener) can read and write the same disk.",[226,227,228],"blockquote",{},[149,229,230,233,234,237,238,241],{},[204,231,232],{},"!IMPORTANT","\nIf you run multiple nodes, this ",[165,235,236],{},"must"," be a shared disk. A connector worker on node A may write ",[186,239,240],{},"inbound\u002F{uuid}\u002Fphoto.jpg",", then a queue job on node B picks up the message and needs to read it. A local disk per node will silently lose files in that handoff.",[145,243,245],{"id":244},"inbound-files","Inbound Files",[149,247,248],{},"When a connector receives a message with files, it generates a UUID for the message and saves each file under:",[195,250,255],{"className":251,"code":253,"language":254},[252],"language-text","{LARACLAW_INCOMING_ATTACHMENTS_PATH}\u002F{uuid}\u002F{filename}\n","text",[186,256,253],{"__ignoreMap":200},[149,258,259,260,263,264,267,268,271,272,274,275,278],{},"The default incoming path is ",[186,261,262],{},"inbound",", so a Telegram photo lands at ",[186,265,266],{},"inbound\u002F01J9...\u002Fphoto.jpg",". The UUID is also written onto the ",[186,269,270],{},"IncomingMessage"," DTO, so any tool the agent runs in response to this message can find its inputs by asking the ",[186,273,60],{}," service for ",[186,276,277],{},"inbound($uuid)",".",[149,280,281],{},"Each connector handles its own download mechanics:",[283,284,285,291,296,301,306],"ul",{},[286,287,288,290],"li",{},[165,289,39],{}," uses the Telegram Bot API to fetch each photo size, voice note, and document.",[286,292,293,295],{},[165,294,43],{}," generates the UUID before downloading so the file API call writes straight into the right folder.",[286,297,298,300],{},[165,299,47],{}," parses the IMAP message and writes each MIME attachment.",[286,302,303,305],{},[165,304,51],{}," accepts uploaded files in the multipart request body.",[286,307,308,310],{},[165,309,55],{}," has no inbound attachments.",[226,312,313],{},[149,314,315,318,319,322],{},[204,316,317],{},"!NOTE","\nInbound folders are not cleaned up automatically. They stay on disk until you remove them. If you want a reaping policy, schedule something against the modified time — for example, \"delete ",[186,320,321],{},"inbound\u002F*"," directories older than 30 days.\"",[145,324,326],{"id":325},"outbound-files","Outbound Files",[149,328,329],{},"Tools that produce files write them to:",[195,331,334],{"className":332,"code":333,"language":254},[252],"{LARACLAW_OUTGOING_ATTACHMENTS_PATH}\u002F{uuid}\u002F{filename}\n",[186,335,333],{"__ignoreMap":200},[149,337,338,339,342,343,346],{},"The default outgoing path is ",[186,340,341],{},"outbound",". Tools never specify a UUID directly — they call ",[186,344,345],{},"$attachments->outbound($message->uuid)->set($filename, $contents)",", and the service computes the path from the current message's UUID. Pretty clean, right?",[149,348,349,350,353],{},"When the agent finishes its turn and the connector is ready to reply, it lists everything in ",[186,351,352],{},"outbound\u002F{uuid}\u002F",", attaches it to the outgoing message, and (for connectors that delete after sending) clears the directory:",[355,356,357,370],"table",{},[358,359,360],"thead",{},[361,362,363,367],"tr",{},[364,365,366],"th",{},"Connector",[364,368,369],{},"Outbound cleanup",[371,372,373,384,392,400,410],"tbody",{},[361,374,375,378],{},[376,377,39],"td",{},[376,379,380,381,383],{},"Deletes ",[186,382,352],{}," after the reply is sent",[361,385,386,388],{},[376,387,43],{},[376,389,380,390,383],{},[186,391,352],{},[361,393,394,396],{},[376,395,47],{},[376,397,380,398,383],{},[186,399,352],{},[361,401,402,404],{},[376,403,51],{},[376,405,406,409],{},[165,407,408],{},"Does not delete"," — your client is responsible for fetching the files and cleaning them up",[361,411,412,414],{},[376,413,55],{},[376,415,380,416,418],{},[186,417,352],{}," after rendering",[145,420,422],{"id":421},"protected-paths","Protected Paths",[149,424,425,426,429,430,433,434,437,438,441,442,445,446,278],{},"Both ",[186,427,428],{},"inbound\u002F"," and ",[186,431,432],{},"outbound\u002F"," are added to ",[186,435,436],{},"BaseTool::isProtectedPath()",". The agent ",[165,439,440],{},"cannot"," list, read, write, move, copy, or delete anything inside them through the ",[443,444,70],"a",{"href":71}," tool — even though the same disk might be allowlisted by ",[186,447,448],{},"LARACLAW_ALLOWED_DISKS",[149,450,451,452,454],{},"Tools that legitimately need to touch attachments (saving an inbound file, queuing an outbound one) do it through the dedicated ",[186,453,60],{}," service, not the File Manager. This separation keeps the agent from accidentally clobbering a delivery in flight.",[149,456,457,458,461,462,464],{},"This is the ",[153,459,460],{},"only"," protection. The rest of the disk is fair game for the File Manager. See ",[443,463,70],{"href":71}," for the details on locking down what else the agent can reach.",[145,466,468],{"id":467},"configuration","Configuration",[195,470,472],{"className":197,"code":471,"language":199,"meta":200,"style":200},"LARACLAW_ATTACHMENTS_DISK=local\nLARACLAW_INCOMING_ATTACHMENTS_PATH=inbound\nLARACLAW_OUTGOING_ATTACHMENTS_PATH=outbound\n",[186,473,474,478,484],{"__ignoreMap":200},[204,475,476],{"class":206,"line":207},[204,477,198],{},[204,479,481],{"class":206,"line":480},2,[204,482,483],{},"LARACLAW_INCOMING_ATTACHMENTS_PATH=inbound\n",[204,485,487],{"class":206,"line":486},3,[204,488,489],{},"LARACLAW_OUTGOING_ATTACHMENTS_PATH=outbound\n",[149,491,492,493,495,496,498],{},"The two path values are folder names within the attachments disk, not disk-relative URIs. Override them if ",[186,494,262],{}," or ",[186,497,341],{}," collides with something else you store on the same disk.",[145,500,502],{"id":501},"whats-next","What's Next",[283,504,505,514,519],{},[286,506,507,509,510,513],{},[443,508,70],{"href":71}," — the tool that reads and writes everywhere ",[153,511,512],{},"except"," these protected folders.",[286,515,516,518],{},[443,517,86],{"href":87}," — generates outbound images.",[286,520,521,524],{},[443,522,523],{"href":75},"Text to Speech"," — generates outbound audio replies.",[149,526,527],{},"Until next time!",[529,530,531],"style",{},"html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}",{"title":200,"searchDepth":480,"depth":480,"links":533},[534,535,536,537,538,539,540,541],{"id":147,"depth":480,"text":5},{"id":174,"depth":480,"text":175},{"id":192,"depth":480,"text":193},{"id":244,"depth":480,"text":245},{"id":325,"depth":480,"text":326},{"id":421,"depth":480,"text":422},{"id":467,"depth":480,"text":468},{"id":501,"depth":480,"text":502},"How Laraclaw stores files coming in from a connector and going back out to one.","md",null,{},true,{"title":60,"description":542},"ZlID5nSX030IB--UP3kmaG4D-1gLTIuPAzYZOG2iIgQ",[550,552],{"title":55,"path":56,"stem":57,"description":551,"children":-1},"Chat with the agent from your shell.",{"title":70,"path":71,"stem":72,"description":553,"children":-1},"Read, write, move, copy, and delete files on any allowed Laravel storage disk.",1786661906119]