{"ok":true,"site":{"host":"app.perpetual.video","url":"https://app.perpetual.video/","type":"live","desc":"A local-first video editor that a browser agent can edit with you.","tools":[{"name":"list_projects","kind":"answer","impl":"imperative","description":"List every project stored in this browser, newest first, and say which one is currently open. Use this before switching or editing so you know what exists.","title":"List projects","annotations":{"readOnlyHint":true,"untrustedContentHint":true},"inputSchema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{},"additionalProperties":false},"hasHandler":true,"hasExecute":true,"__source":"spec","executable":true,"handlerField":"execute"},{"name":"create_project","kind":"act","impl":"imperative","description":"Create a new, empty project and open it. The editing tools become available once a project is open. The imported media library is shared across projects.","title":"Create project","inputSchema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":120}},"additionalProperties":false},"hasHandler":true,"hasExecute":true,"__source":"spec","executable":true,"handlerField":"execute"},{"name":"switch_project","kind":"act","impl":"imperative","description":"Open a different project by id. The timeline, history and activity feed are replaced by that project's own. Undo does not cross projects.","title":"Switch project","inputSchema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"projectId":{"type":"string","minLength":1}},"required":["projectId"],"additionalProperties":false},"hasHandler":true,"hasExecute":true,"__source":"spec","executable":true,"handlerField":"execute"},{"name":"rename_project","kind":"act","impl":"imperative","description":"Rename a project. Defaults to the project that is currently open.","title":"Rename project","inputSchema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":120},"projectId":{"type":"string","minLength":1}},"required":["name"],"additionalProperties":false},"hasHandler":true,"hasExecute":true,"__source":"spec","executable":true,"handlerField":"execute"},{"name":"duplicate_project","kind":"act","impl":"imperative","description":"Copy a project's whole timeline into a new project and open it. Useful before trying a variant edit, since it keeps the original intact.","title":"Duplicate project","inputSchema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"projectId":{"type":"string","minLength":1},"name":{"type":"string","minLength":1,"maxLength":120}},"additionalProperties":false},"hasHandler":true,"hasExecute":true,"__source":"spec","executable":true,"handlerField":"execute"},{"name":"delete_project","kind":"transact","impl":"imperative","description":"Permanently delete a project and its timeline. This cannot be undone. Imported media is kept, because the library is shared with other projects. Requires the project id, so confirm with the user which one you mean.","title":"Delete project","inputSchema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"projectId":{"type":"string","minLength":1}},"required":["projectId"],"additionalProperties":false},"hasHandler":true,"hasExecute":true,"__source":"spec","executable":true,"handlerField":"execute"},{"name":"get_editor_state","kind":"answer","impl":"imperative","description":"Read the open project: canvas settings, timeline length, playhead and the overlays on the timeline. Overlays are returned 12 at a time; when `overlaysTruncated` is true, call again with a higher `offset` to page through the rest. `overlayCount` is the true total.","title":"Get editor state","annotations":{"readOnlyHint":true,"untrustedContentHint":true},"inputSchema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"offset":{"type":"integer","minimum":0,"maximum":1000},"limit":{"type":"integer","minimum":1,"maximum":50}},"additionalProperties":false},"hasHandler":true,"hasExecute":true,"__source":"spec","executable":true,"handlerField":"execute"},{"name":"list_assets","kind":"answer","impl":"imperative","description":"List the media the user imported or generated in this browser. Use the returned assetId with add_asset.","title":"List imported media","annotations":{"readOnlyHint":true,"untrustedContentHint":true},"inputSchema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{},"additionalProperties":false},"hasHandler":true,"hasExecute":true,"__source":"spec","executable":true,"handlerField":"execute"},{"name":"seek","kind":"act","impl":"imperative","description":"Move the playhead of the preview to a time in seconds.","title":"Move the playhead","inputSchema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"timeSeconds":{"type":"number","minimum":0,"maximum":36000}},"required":["timeSeconds"],"additionalProperties":false},"hasHandler":true,"hasExecute":true,"__source":"spec","executable":true,"handlerField":"execute"},{"name":"add_text","kind":"act","impl":"imperative","description":"Add a text overlay to the timeline. Times are in seconds and default to fromSeconds 0 for 3 seconds. Position and size are in canvas pixels — call get_editor_state for the current canvas dimensions — and default to a centred band across 80% of the width. `row` is the timeline layer, 0 on top. Overlapping overlays on the same row are pushed apart automatically.","title":"Add a title","inputSchema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"content":{"type":"string","minLength":1,"maxLength":500},"fromSeconds":{"type":"number","minimum":0,"maximum":3600},"durationSeconds":{"type":"number","minimum":0.1,"maximum":3600},"row":{"type":"integer","minimum":0,"maximum":7},"left":{"type":"number","minimum":-20000,"maximum":20000},"top":{"type":"number","minimum":-20000,"maximum":20000},"width":{"type":"number","minimum":1,"maximum":20000},"height":{"type":"number","minimum":1,"maximum":20000},"rotation":{"type":"number","minimum":-3600,"maximum":3600},"fontSize":{"type":"string","maxLength":20},"color":{"type":"string","maxLength":64},"textAlign":{"type":"string","enum":["left","center","right"]}},"required":["content"],"additionalProperties":false},"hasHandler":true,"hasExecute":true,"__source":"spec","executable":true,"handlerField":"execute"},{"name":"add_shape","kind":"act","impl":"imperative","description":"Add a shape overlay to the timeline. Times are in seconds and default to fromSeconds 0 for 3 seconds. Position and size are in canvas pixels — call get_editor_state for the current canvas dimensions — and default to a centred box. `row` is the timeline layer, 0 on top. Overlapping overlays on the same row are pushed apart automatically.","title":"Add a shape","inputSchema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"shape":{"type":"string","enum":["rectangle","rounded-rect","circle","ellipse","triangle","diamond","pentagon","hexagon","arrow-right","arrow-left","arrow-up","arrow-down","double-arrow","line-horizontal","line-vertical","heart","cross","ring","chevron-right","chevron-left","speech-bubble","thought-bubble"]},"fromSeconds":{"type":"number","minimum":0,"maximum":3600},"durationSeconds":{"type":"number","minimum":0.1,"maximum":3600},"row":{"type":"integer","minimum":0,"maximum":7},"left":{"type":"number","minimum":-20000,"maximum":20000},"top":{"type":"number","minimum":-20000,"maximum":20000},"width":{"type":"number","minimum":1,"maximum":20000},"height":{"type":"number","minimum":1,"maximum":20000},"rotation":{"type":"number","minimum":-3600,"maximum":3600},"fill":{"type":"string","maxLength":64}},"required":["shape"],"additionalProperties":false},"hasHandler":true,"hasExecute":true,"__source":"spec","executable":true,"handlerField":"execute"},{"name":"add_asset","kind":"act","impl":"imperative","description":"Put an imported or generated asset on the timeline. Call list_assets first to get an assetId. Give sourceStartSeconds and sourceEndSeconds to place only one part of a video or sound, which is how a moment found with search_content is cut out of the original file.","title":"Add media to the timeline","annotations":{"untrustedContentHint":true},"inputSchema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"assetId":{"type":"string","minLength":8,"maxLength":64},"sourceStartSeconds":{"type":"number","minimum":0,"maximum":36000},"sourceEndSeconds":{"type":"number","minimum":0,"maximum":36000},"fromSeconds":{"type":"number","minimum":0,"maximum":3600},"durationSeconds":{"type":"number","minimum":0.1,"maximum":3600},"row":{"type":"integer","minimum":0,"maximum":7}},"required":["assetId"],"additionalProperties":false},"hasHandler":true,"hasExecute":true,"__source":"spec","executable":true,"handlerField":"execute"},{"name":"update_overlay","kind":"act","impl":"imperative","description":"Change one overlay: its timing, row, position, size, rotation, text or style. sourceStartSeconds re-points a video or sound at a different part of its own file. To move more than one overlay, use arrange_timeline instead.","title":"Change an overlay","annotations":{"untrustedContentHint":true},"inputSchema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"overlayId":{"type":"integer","exclusiveMinimum":0,"maximum":9007199254740991},"fromSeconds":{"type":"number","minimum":0,"maximum":3600},"durationSeconds":{"type":"number","minimum":0.1,"maximum":3600},"row":{"type":"integer","minimum":0,"maximum":7},"left":{"type":"number","minimum":-20000,"maximum":20000},"top":{"type":"number","minimum":-20000,"maximum":20000},"width":{"type":"number","minimum":1,"maximum":20000},"height":{"type":"number","minimum":1,"maximum":20000},"rotation":{"type":"number","minimum":-3600,"maximum":3600},"content":{"type":"string","maxLength":2000},"sourceStartSeconds":{"type":"number","minimum":0,"maximum":36000},"opacity":{"type":"number","minimum":0,"maximum":1},"volume":{"type":"number","minimum":0,"maximum":1},"speed":{"type":"number","minimum":0.25,"maximum":8},"color":{"type":"string","maxLength":64},"fontSize":{"type":"string","maxLength":20}},"required":["overlayId"],"additionalProperties":false},"hasHandler":true,"hasExecute":true,"__source":"spec","executable":true,"handlerField":"execute"},{"name":"arrange_timeline","kind":"act","impl":"imperative","description":"Set the start time, and optionally the row, of several overlays in one step. Use this to reorder a sequence: moving overlays one at a time makes each move push the others aside, because the timeline closes overlaps after every change.","title":"Move several overlays at once","inputSchema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"placements":{"minItems":1,"maxItems":24,"type":"array","items":{"type":"object","properties":{"overlayId":{"type":"integer","exclusiveMinimum":0,"maximum":9007199254740991},"fromSeconds":{"type":"number","minimum":0,"maximum":36000},"row":{"type":"integer","minimum":0,"maximum":7}},"required":["overlayId","fromSeconds"],"additionalProperties":false}}},"required":["placements"],"additionalProperties":false},"hasHandler":true,"hasExecute":true,"__source":"spec","executable":true,"handlerField":"execute"},{"name":"duplicate_overlay","kind":"act","impl":"imperative","description":"Copy one overlay, placing the copy immediately after the original on the same row. Useful for repeating a title or a shape without restating all of its styling.","title":"Duplicate an overlay","annotations":{"untrustedContentHint":true},"inputSchema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"overlayId":{"type":"integer","exclusiveMinimum":0,"maximum":9007199254740991}},"required":["overlayId"],"additionalProperties":false},"hasHandler":true,"hasExecute":true,"__source":"spec","executable":true,"handlerField":"execute"},{"name":"delete_overlay","kind":"transact","impl":"imperative","description":"Remove one overlay from the timeline. The user can undo this from the activity panel.","title":"Delete an overlay","inputSchema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"overlayId":{"type":"integer","exclusiveMinimum":0,"maximum":9007199254740991}},"required":["overlayId"],"additionalProperties":false},"hasHandler":true,"hasExecute":true,"__source":"spec","executable":true,"handlerField":"execute"},{"name":"split_overlay","kind":"act","impl":"imperative","description":"Cut one overlay at a point on the timeline, leaving two overlays that play back to back. Use this to drop an unwanted stretch: cut at both ends of it, then delete the middle piece.","title":"Cut an overlay in two","inputSchema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"overlayId":{"type":"integer","exclusiveMinimum":0,"maximum":9007199254740991},"timeSeconds":{"type":"number","minimum":0,"maximum":36000}},"required":["overlayId","timeSeconds"],"additionalProperties":false},"hasHandler":true,"hasExecute":true,"__source":"spec","executable":true,"handlerField":"execute"},{"name":"analyze_assets","kind":"act","impl":"imperative","description":"Send local media to Gemini so its content can be searched: shots, spoken words and subjects, each with a time range. The user must approve the upload. The index is then stored in this browser, so an asset only needs analysing once. Call search_content afterwards.","title":"Index media with Gemini","annotations":{"untrustedContentHint":true},"inputSchema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"assetIds":{"minItems":1,"maxItems":10,"type":"array","items":{"type":"string","minLength":8,"maxLength":64}}},"required":["assetIds"],"additionalProperties":false},"hasHandler":true,"hasExecute":true,"__source":"spec","executable":true,"handlerField":"execute"},{"name":"get_asset_analysis","kind":"answer","impl":"imperative","description":"Return the stored index for one asset: a summary, tags, and every segment with its start and end time, description and speech. Use the times with add_asset to place just that part.","title":"Read what is in one asset","annotations":{"readOnlyHint":true,"untrustedContentHint":true},"inputSchema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"assetId":{"type":"string","minLength":8,"maxLength":64}},"required":["assetId"],"additionalProperties":false},"hasHandler":true,"hasExecute":true,"__source":"spec","executable":true,"handlerField":"execute"},{"name":"search_content","kind":"answer","impl":"imperative","description":"Search everything that has been analysed and return the matching moments, each with its asset and its start and end time. Pass those times to add_asset to place only that moment.","title":"Find a moment in the media","annotations":{"readOnlyHint":true,"untrustedContentHint":true},"inputSchema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"query":{"type":"string","minLength":2,"maxLength":200},"limit":{"type":"integer","minimum":1,"maximum":30}},"required":["query"],"additionalProperties":false},"hasHandler":true,"hasExecute":true,"__source":"spec","executable":true,"handlerField":"execute"},{"name":"set_background","kind":"act","impl":"imperative","description":"Set the canvas background to a solid colour or a gradient.","title":"Set the background","inputSchema":{"$schema":"https://json-schema.org/draft/2020-12/schema","oneOf":[{"type":"object","properties":{"type":{"type":"string","const":"color"},"color":{"type":"string","minLength":1,"maxLength":64}},"required":["type","color"],"additionalProperties":false},{"type":"object","properties":{"type":{"type":"string","const":"gradient"},"gradient":{"type":"object","properties":{"direction":{"type":"string","minLength":1,"maxLength":64},"colors":{"minItems":2,"maxItems":8,"type":"array","items":{"type":"string","minLength":1,"maxLength":64}}},"required":["direction","colors"],"additionalProperties":false}},"required":["type","gradient"],"additionalProperties":false}]},"hasHandler":true,"hasExecute":true,"__source":"spec","executable":true,"handlerField":"execute"},{"name":"set_aspect_ratio","kind":"act","impl":"imperative","description":"Set the canvas shape to 16:9, 9:16, 1:1 or 4:5.","title":"Set the aspect ratio","inputSchema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"aspectRatio":{"type":"string","enum":["16:9","9:16","1:1","4:5"]}},"required":["aspectRatio"],"additionalProperties":false},"hasHandler":true,"hasExecute":true,"__source":"spec","executable":true,"handlerField":"execute"},{"name":"undo","kind":"act","impl":"imperative","description":"Undo the most recent change, whoever made it.","title":"Undo","inputSchema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{},"additionalProperties":false},"hasHandler":true,"hasExecute":true,"__source":"spec","executable":true,"handlerField":"execute"},{"name":"redo","kind":"act","impl":"imperative","description":"Redo the change that was undone last.","title":"Redo","inputSchema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{},"additionalProperties":false},"hasHandler":true,"hasExecute":true,"__source":"spec","executable":true,"handlerField":"execute"},{"name":"import_from_url","kind":"act","impl":"imperative","description":"Fetch video, audio or images from public URLs and store them in this browser's media library. Use this whenever you have a link to a file rather than a file — an agent cannot put media into the user's browser any other way. Pass up to 8 URLs at once. The kind and duration are detected automatically. This does not put anything on the timeline; call add_asset with a returned assetId next. The URL must point straight at the file and the host must allow other sites to read it, which most CDNs and object stores do; a link to a web page that merely displays the media will not work.","title":"Import hosted media by URL","annotations":{"untrustedContentHint":true},"inputSchema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"urls":{"minItems":1,"maxItems":8,"type":"array","items":{"type":"string","minLength":1,"maxLength":2048}}},"required":["urls"],"additionalProperties":false},"hasHandler":true,"hasExecute":true,"__source":"spec","executable":true,"handlerField":"execute"},{"name":"generate_image","kind":"act","impl":"imperative","description":"Generate an image from a prompt with the user's own Gemini key and save it as a local asset. The user must approve the request, which is paid. This does not put the image on the timeline; call add_asset with the returned assetId next.","title":"Generate an image with Gemini","annotations":{"untrustedContentHint":true},"inputSchema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"prompt":{"type":"string","minLength":1,"maxLength":2000},"aspectRatio":{"type":"string","enum":["16:9","9:16","1:1","4:5"]}},"required":["prompt"],"additionalProperties":false},"hasHandler":true,"hasExecute":true,"__source":"spec","executable":true,"handlerField":"execute"},{"name":"generate_video","kind":"act","impl":"imperative","description":"Generate a short video clip from a prompt with the user's own Gemini key, and save it as a local asset. The user must approve the request, which is paid and takes one to three minutes. This does not put the clip on the timeline; call add_asset with the returned assetId next. The result is ordinary local media, so analyze_assets and add_kinetic_captions work on it too.","title":"Generate a video with Gemini","annotations":{"untrustedContentHint":true},"inputSchema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"prompt":{"type":"string","minLength":1,"maxLength":2000},"aspectRatio":{"type":"string","enum":["16:9","9:16"]},"durationSeconds":{"anyOf":[{"type":"number","const":4},{"type":"number","const":6},{"type":"number","const":8}]}},"required":["prompt"],"additionalProperties":false},"hasHandler":true,"hasExecute":true,"__source":"spec","executable":true,"handlerField":"execute"},{"name":"add_kinetic_captions","kind":"act","impl":"imperative","description":"Transcribe the speech in one video clip and lay it over that clip as animated captions. Each line is typeset with its key phrase large, revealed word by word in time with the speech, placed to graze the subject, and masked so words pass behind the person on screen. This is a single finished effect — do not try to reproduce it with add_text. The user must approve the upload to Gemini, and it takes around a minute.","title":"Caption a clip in the kinetic style","annotations":{"untrustedContentHint":true},"inputSchema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"overlayId":{"type":"integer","exclusiveMinimum":0,"maximum":9007199254740991},"fromSeconds":{"type":"number","minimum":0,"maximum":36000},"toSeconds":{"type":"number","minimum":0,"maximum":36000},"maskSubject":{"type":"boolean"},"color":{"type":"string","maxLength":64},"fontFamily":{"type":"string","enum":["serif","sans"]}},"required":["overlayId"],"additionalProperties":false},"hasHandler":true,"hasExecute":true,"__source":"spec","executable":true,"handlerField":"execute"},{"name":"list_generations","kind":"answer","impl":"imperative","description":"List the image and video requests that are still running or that failed, whoever started them. A finished generation is not listed here: it becomes an asset, so call list_assets for it.","title":"List generations in progress","annotations":{"readOnlyHint":true,"untrustedContentHint":true},"inputSchema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{},"additionalProperties":false},"hasHandler":true,"hasExecute":true,"__source":"spec","executable":true,"handlerField":"execute"},{"name":"list_workflows","kind":"answer","impl":"imperative","description":"List the workflows captured in this browser. A workflow describes what someone did in a project and why — a reusable approach, not a script. Use it to find a relevant one when the user refers to how something was done before, then read it with get_workflow.","title":"List workflows","annotations":{"readOnlyHint":true,"untrustedContentHint":true},"inputSchema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{},"additionalProperties":false},"hasHandler":true,"hasExecute":true,"__source":"spec","executable":true,"handlerField":"execute"},{"name":"get_workflow","kind":"answer","impl":"imperative","description":"Read a workflow in full: the intent behind it, the method, the ordered pattern, the conventions worth preserving, guidance on adapting it, and the normalised record of what was actually done. There is no replay tool and that is deliberate — apply the pattern to the project that is open now using the editing tools, re-deriving timing, counts and geometry from the current state. Geometry in the record is a fraction of the canvas, so it carries across aspect ratios; element references point at the action that created the element.","title":"Get a workflow","annotations":{"readOnlyHint":true,"untrustedContentHint":true},"inputSchema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"workflowId":{"type":"string","minLength":1}},"required":["workflowId"],"additionalProperties":false},"hasHandler":true,"hasExecute":true,"__source":"spec","executable":true,"handlerField":"execute"},{"name":"create_workflow_from_project","kind":"act","impl":"imperative","description":"Analyse what was done in a project and capture it as a workflow. The action log is distilled deterministically — reads dropped, deleted work dropped, intermediate adjustments collapsed, geometry normalised to canvas fractions — then analysed to describe the intent, method and pattern. Needs a Gemini key, or the shared demo key when one is available. Defaults to the open project.","title":"Capture a workflow from a project","annotations":{"untrustedContentHint":true},"inputSchema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"projectId":{"type":"string","minLength":1}},"additionalProperties":false},"hasHandler":true,"hasExecute":true,"__source":"spec","executable":true,"handlerField":"execute"},{"name":"delete_workflow","kind":"transact","impl":"imperative","description":"Permanently delete a workflow. This cannot be undone. Requires the workflow id.","title":"Delete a workflow","inputSchema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"workflowId":{"type":"string","minLength":1}},"required":["workflowId"],"additionalProperties":false},"hasHandler":true,"hasExecute":true,"__source":"spec","executable":true,"handlerField":"execute"},{"name":"get_project_events","kind":"answer","impl":"imperative","description":"The raw, ordered log of editing actions for a project, in tool vocabulary. get_workflow is usually the better read — it is already analysed. Reach for this when you need ground truth about a project that has no workflow captured yet. Paged: pass offset to continue.","title":"Get a project's action log","annotations":{"readOnlyHint":true,"untrustedContentHint":true},"inputSchema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"projectId":{"type":"string","minLength":1},"offset":{"type":"integer","minimum":0,"maximum":100000},"limit":{"type":"integer","minimum":1,"maximum":200}},"additionalProperties":false},"hasHandler":true,"hasExecute":true,"__source":"spec","executable":true,"handlerField":"execute"}],"category":"Media & Personal","favicon":"/favicons/app.perpetual.video.png","apiSurface":"spec","toolCount":34}}