{"name":"Sundial","description":"Collaborative workspaces with files, presence, provenance gutters, and live CRDT documents.","api_base":"https://www.sundial.md/api","start_url":"https://www.sundial.md/start","docs_url":"https://www.sundial.md/agent-docs","skill_url":"https://www.sundial.md/start","templates_url":"https://www.sundial.md/api/templates","assistants_url":"https://www.sundial.md/api/assistants","mcp_url":"https://www.sundial.md/mcp","capabilities":["create_workspace","connect_assistant","list_files","grep","read_file","write_file","edit_file","delete_file","move_file","presence","exec","comments","collab"],"auth":{"methods":["bearer"],"api_key_header":"Authorization: Bearer <token>","shared_link":{"token_from_url":"?token=<token>","preferred_header":"Authorization: Bearer <token>"},"attribution_header":"X-Agent-Id: ai:<your-agent-name>"},"endpoints":{"create_workspace":{"method":"POST","url":"https://www.sundial.md/api/templates/new","body":{"slug":"<template-slug>"},"description":"Create a workspace from a template with no account and no token. Send a Cookie header with a self-minted sd_anon=<16 chars of [a-z0-9]>; whoever holds that value owns the workspace until a human claims it by signing in. Returns {project:{id, open_url:\"/w/<public-id>?fresh=1&anon=<your-sd_anon>\"}}. open_url is a PATH — prefix this manifest's origin to get the link you hand the human, and keep its &anon= intact: that transfers the ownership identity to their browser so sign-in claims the workspace. GET /api/templates lists the slugs. Combine up to 4 with {slugs:[...], layout:\"main\"|\"folders\"}."},"connect_assistant":{"method":"POST","url":"https://www.sundial.md/api/workspace/assistants/connect","body":{"projectId":"<workspace-uuid>","slug":"<assistant-slug>"},"description":"Connect an assistant (template) to an EXISTING workspace: seeds its files at the workspace root (a taken name gets a -<slug> suffix, an existing AGENTS.md gets the assistant's section appended, nothing is overwritten) and appends its instructions to the workspace's agent context. Requires write access (bearer token or owning sd_anon cookie). GET /api/assistants lists the slugs."},"join":{"method":"POST","url":"https://www.sundial.md/api/workspace/local-agent/join","description":"Mints a 7-day bearer token + bootstrap prompt for a workspace the caller already has access to. Usually called by the web app, but an agent that created its own workspace can call it directly with the same sd_anon cookie to get a writable token."},"presence":{"method":"POST","url":"https://www.sundial.md/api/workspace/local-agent/presence","description":"Heartbeat: presence chip stays alive for ~10min after any call. Any action above already counts; only send a re-ping (every few minutes) if you sit idle and want the chip to stay."},"list_files":{"method":"GET","url":"https://www.sundial.md/api/workspace/local-agent/files?workspaceId=<id>","description":"Whole tree by default. Scope with &glob=<pattern> (**/*.ts, src/**, *.md) or &path=<dir> for a subtree — bounded, no sandbox."},"grep":{"method":"GET","url":"https://www.sundial.md/api/workspace/local-agent/grep?workspaceId=<id>&pattern=<regex>","description":"Server-side regex search over file contents (POSIX ARE), scoped with &path=<dir>, case-insensitive with &i=1. Mirrors Sundial Agent's Grep — runs in Postgres, no sandbox. Returns {matches:[{path,line,text}], overflow, output}."},"read_file":{"method":"GET","url":"https://www.sundial.md/api/workspace/local-agent/file?workspaceId=<id>&path=<path>"},"write_file":{"method":"PUT","url":"https://www.sundial.md/api/workspace/local-agent/file","body":{"workspaceId":"<id>","path":"<path>","content":"<text>"},"attribution":"Writes are recorded with actor=local_agent and author_id=<X-Agent-Id>. The provenance gutter shows them under that name.","description":"Writes land as a pending suggestion humans review (accept/reject) by default — suggest unless the human asks you to apply directly, then add \"editMode\":\"edit\" to write the doc directly. Humans can lock the whole connection to suggest (agent-chip switch or suggest-only token)."},"edit_file":{"method":"POST","url":"https://www.sundial.md/api/workspace/local-agent/file/edit","body":{"workspaceId":"<id>","path":"<path>","edits":[{"old_string":"...","new_string":"...","replace_all":false}]},"description":"Sequential find/replace edits in one round trip — matches Sundial Agent edit/multiedit + Claude Code Edit/MultiEdit. Errors: 409 ANCHOR_NOT_FOUND, 409 AMBIGUOUS_ANCHOR."},"delete_file":{"method":"DELETE","url":"https://www.sundial.md/api/workspace/local-agent/file","body":{"workspaceId":"<id>","path":"<path>"},"description":"Delete a file or a whole folder subtree (uploaded blobs included). Deleting a missing path is an idempotent ok."},"move_file":{"method":"PATCH","url":"https://www.sundial.md/api/workspace/local-agent/file","body":{"workspaceId":"<id>","sourcePath":"<path>","targetPath":"<path>"},"description":"Move/rename a file or folder subtree. Errors: 404 unknown source, 409 target already exists."},"exec":{"method":"POST","url":"https://www.sundial.md/api/workspace/local-agent/exec","body":{"workspaceId":"<id>","command":"<bash>","timeoutSeconds":30},"description":"Run a bash command in a per-agent Modal sandbox. Workspace files are hydrated; edits flow through record_text_edit with actor=local_agent and author_id=<X-Agent-Id>. Sandbox file writes follow your edit mode: suggestions by default, or add \"editMode\":\"edit\" to apply directly (deletes/renames always apply directly). Sandbox is keyed on (workspace_id, agent_id) and reused; idle TTL is 15 minutes."},"comments_create":{"method":"POST","url":"https://www.sundial.md/api/workspace/local-agent/comments","body":{"workspaceId":"<id>","path":"<path>","quote":"<text>","body":"<comment>"},"description":"Open a comment thread anchored to a quoted span. Returns 409 ANCHOR_NOT_FOUND if the quote no longer appears in the file. The thread shows up in the workspace comments panel alongside human-authored threads."},"comments_list":{"method":"GET","url":"https://www.sundial.md/api/workspace/local-agent/comments?workspaceId=<id>&path=<optional path>","description":"List comment threads for the workspace, optionally filtered by file path. Includes all messages and author info."},"comments_reply":{"method":"POST","url":"https://www.sundial.md/api/workspace/local-agent/comments/<threadId>/messages","body":{"workspaceId":"<id>","body":"<reply>"},"description":"Append a message to an open thread. Returns 409 THREAD_RESOLVED if the thread has been closed."},"comments_resolve":{"method":"POST","url":"https://www.sundial.md/api/workspace/local-agent/comments/<threadId>/resolve","body":{"workspaceId":"<id>"},"description":"Resolve a thread. Append ?action=reopen to reopen one. Use sparingly — humans typically own resolution."},"collab_session":{"method":"GET","url":"https://www.sundial.md/api/workspace/local-agent/collab-session?workspaceId=<id>","description":"Optional. Hands back a Hocuspocus WS ticket (CollabSessionInfo: syncProtocol pm-yjs-v1, collabWsUrl, token, docNamePrefix, awareness). Pair with the bundled sundial-agent-ws.mjs helper (or any y-protocol client) to render a real in-doc cursor next to humans. HTTP endpoints work without this."}},"quickstart":{"no_workspace":{"description":"Starting from nothing. Mint a random sd_anon value (16 chars of [a-z0-9]) and send it as a Cookie on both calls: POST /api/templates/new creates the workspace, POST /api/workspace/local-agent/join returns a 7-day bearer token for it. Then continue exactly as received_link. Give the human the workspace URL from open_url.","steps":[{"method":"POST","url":"https://www.sundial.md/api/templates/new","body":{"slug":"<template-slug>"}},{"method":"POST","url":"https://www.sundial.md/api/workspace/local-agent/join","body":{"projectId":"<project.id>"}}],"full_instructions":"https://www.sundial.md/start"},"received_link":{"description":"Given a Sundial workspace URL with ?token=<token>, connect with ONE call: GET /files registers your presence (the human sees your chip) and returns the file tree in the same response. No separate presence-ping needed to join. (`state` is a back-compat alias of `connect`; `presence` is an optional idle keepalive.)","connect":{"method":"GET","url":"https://www.sundial.md/api/workspace/local-agent/files?workspaceId=<id>","headers":{"Authorization":"Bearer <token>","X-Agent-Id":"ai:<your-agent-name>"}},"state":{"method":"GET","url":"https://www.sundial.md/api/workspace/local-agent/files?workspaceId=<id>","headers":{"Authorization":"Bearer <token>","X-Agent-Id":"ai:<your-agent-name>"}},"presence":{"method":"POST","url":"https://www.sundial.md/api/workspace/local-agent/presence","headers":{"Authorization":"Bearer <token>","X-Agent-Id":"ai:<your-agent-name>","Content-Type":"application/json"},"body":{"workspaceId":"<id>"}}}}}