Steps and tools.

The complete surface under gp. An edit step changes the project and stages with the script's other edits. A tool reads, analyses, imports or exports. Each entry gives the call, its arguments and what it does, generated from the stub Grapple publishes.

Use a step or a tool

gp.step("timeline.edit", operation="split", clipNodeIds=[clip.id], time=3.0)
gp.step.help("timeline.edit")        # the full contract, in the script's output
rows = gp.tool("project.query", select=gp.comp("Main"), fields=["name", "kind"])
  • Arguments use the names the contract registers, which are camelCase, such as clipNodeIds. Pass a handle where an id is wanted and its id is used.
  • A call returns a gp.Result. Its .id and .object give the object the call created, when it creates one.
  • In a step, a string starting with $ refers to an earlier step's result; $$ writes a literal dollar sign.
  • Which steps and tools a run can call depends on what that run is allowed to do. help(gp.step) and help(gp.tool) list the ones available to it.

Edit steps

Every step stages inside a script with its other edits, and commits with them as one History row.

gp.step("asset.rename", assetId=…, name=…)

Rename a project asset while keeping its media and every use.

Takes assetId, name.

gp.step("camera.create", name=…, …)

Create a camera in a composition. It drives the output unless makeActive is false; animate it with the set step like a layer.

Takes name and optionally compositionNodeId, focalLength, makeActive, shutterAngleDegrees.

gp.step("camera.set_active", cameraNodeId=…, compositionNodeId=…)

Select the camera that drives composition output.

Takes cameraNodeId, compositionNodeId.

gp.step("camera.update", cameraNodeId=…, name=…)

Rename an existing camera. Use the set step in project.edit to edit its lens properties.

Takes cameraNodeId, name.

gp.step("collection.author_field_effector", activeRange=…, displayName=…, elements=…, falloff=…, field=…, mode=…, strength=…, targetAttribute=…)

Create a field effector for a generated shape collection. Connect the collection's elements and a scalar field, then choose the affected attribute, mode, strength, falloff, and active range. Returns the effect node and its output port.

Takes activeRange, displayName, elements, falloff, field, mode, strength, targetAttribute.

gp.step("collection.breakout", collectionNodeId=…)

Replace the collection with its members as ordinary clips, keeping each member's node id, its layout as a controller, and the collection's animation and effects copied to each member.

Takes collectionNodeId.

gp.step("collection.create", generator=…, templates=…, …)

Turn clips on one layer into a collection whose members are generated from them. A layout places one member per template; repeat makes count copies of one template in a row, grid or circle. Positions and lengths use composition pixels from the top left, with x right and y down; scale and opacity use percent, and rotation uses degrees. Set or key collection Params with the set step in project.edit. Returns each member as {owner, kind: "member", key, label}.

Takes generator, templates and optionally params.

gp.step("collection.override", collectionNodeId=…, member=…, propertyId=…, …)

Set one registered property on a collection member by its key, or omit value to clear the override. The value replaces that member's template value; placement properties replace the layout value. An override remains available if its member key returns later.

Takes collectionNodeId, member, propertyId and optionally componentIndex, value.

gp.step("collection.set_generator", collectionNodeId=…, generator=…)

Replace a collection's generator: its kind and the fields that kind uses, such as a repeat's count and arrangement or a grid's columns. Params the new generator reads are added with their defaults and existing Params keep their values. Overrides are kept; one on a key the generator no longer makes applies again when the key returns.

Takes collectionNodeId, generator.

gp.step("composition.create", frameRate=…, name=…, outputResolution=…, …)

Create a composition: its name, frame size, frame rate, and optionally its duration (10 seconds by default) and working range.

Takes frameRate, name, outputResolution and optionally duration, workingRange.

gp.step("composition.update", compositionNodeId=…, …)

Change a composition's name, size, frame rate, duration, working range, output selection, or 3D lights. A null duration makes the composition as long as its content.

Takes compositionNodeId and optionally duration, frameRate, name, outputResolution, scene3D, setOutput, workingRange.

gp.step("control.configure_surface", compositionId=…, surface=…)

Create two to four sets per composition. Name sets by intent in the person's words, such as Entrance, Arrangement, Finish or Mood, never by object, layer type or technique. Put two to six controls in each set, most consequential first. Publish one feel control per movement: use the progress input of the rig that drives the whole movement, and build that input first when it does not exist rather than publishing X and Y separately. A native transform is published whole: Position, Scale and Anchor are one control each, carrying X and Y, or X, Y and Z once the object is a 3D layer. Label scope honestly, such as Title X entrance when only X moves. Keep labels short enough to share a line with a caption, about 24 characters, and descriptions to one sentence, about 90 characters, saying what changes on screen and what is out of scope. Publish palette colors as adjacent controls in reading order. Never publish a value twice or a source outside the composition. Read control.inspect_surface before replacing the surface; keep every ID and the person's names and order. Keep values and keys the person changed unless the request asks for them to change, and say so in the reply when it does.

Takes compositionId, surface.

gp.step("delete", refs=…, …)

Delete objects and owned children, preserving surviving placement where possible.

Takes refs and optionally at, expect, include, mode.

gp.step("effect.connect_ports", sourceNodeId=…, sourcePort=…, targetNodeId=…, targetPort=…, …)

Connect compatible effect graph ports.

Takes sourceNodeId, sourcePort, targetNodeId, targetPort and optionally order, routeMemberKind.

gp.step("effect.create", arguments=…, builtin=…, …)

Create a builtin effect from its inspected fields.

Takes arguments, builtin and optionally ifExists.

gp.step("effect.disconnect_ports", edgeId=…)

Disconnect an effect graph edge.

Takes edgeId.

gp.step("effect.replace_input_source", edgeId=…, sourceNodeId=…, sourcePort=…)

Replace an input edge's source.

Takes edgeId, sourceNodeId, sourcePort.

gp.step("effect.set_bypass", bypassed=…, effectNodeId=…)

Turn a frame effect off or back on without removing it.

Takes bypassed, effectNodeId.

gp.step("effect.set_input_route_role", edgeId=…, role=…)

Set an input edge's semantic route role.

Takes edgeId, role.

gp.step("effect.update_source", effectNodeId=…, …)

Replace an authored effect's source contract.

Takes effectNodeId and optionally activeRange, contextResources, displayName, entrypoint, inputPorts, language, outputPorts, params, pythonPixels, resources, role, source, sourcePath.

gp.step("marker.create", label=…, ownerNodeId=…, time=…)

Create a durable marker on a composition or track.

Takes label, ownerNodeId, time.

gp.step("marker.update", change=…, markerKey=…, ownerNodeId=…)

Change a durable marker's time or label.

Takes change, markerKey, ownerNodeId.

gp.step("note.create", markdown=…, title=…, …)

Create a project note, optionally attached to one exact node or asset.

Takes markdown, title and optionally attachment.

gp.step("note.update", noteNodeId=…, …)

Change selected note fields or its exact node/asset attachment.

Takes noteNodeId and optionally attachment, markdown, title.

gp.step("particle.author", activeRange=…, color=…, compositionNodeId=…, displayName=…, dragPerSecond=…, emissionRate=…, emitter=…, fadeFraction=…, fixedStep=…, gravity=…, initialVelocity=…, lifetimeSeconds=…, maximumCount=…, radius=…, seed=…, trackNodeId=…, …)

Create and place particles on a visual track. Choose a point, line, or rectangle emitter, a continuous rate or initial burst, and optional ranges for direction, speed, lifetime, size, and opacity. Coordinates and speeds use composition pixels; positive y points down. Returns the effect, track, and bake details.

Takes activeRange, color, compositionNodeId, displayName, dragPerSecond, emissionRate, emitter, fadeFraction, fixedStep, gravity, initialVelocity, lifetimeSeconds, maximumCount, radius, seed, trackNodeId and optionally burstCount, directionSpreadDegrees, lifetimeRange, opacityRange, sizeRange, speedRange.

gp.step("particle.rebake", effectNodeId=…)

Update the particle simulation after changing its editable parameters.

Takes effectNodeId.

gp.step("pose.author", poses=…, rigEffectNodeId=…)

Save named rig poses.

Takes poses, rigEffectNodeId.

gp.step("pose.blend", amount=…, easingToNext=…, rigEffectNodeId=…, sourceTimeA=…, sourceTimeB=…, time=…, …)

Blend saved rig poses into animation.

Takes amount, easingToNext, rigEffectNodeId, sourceTimeA, sourceTimeB, time and optionally controlIds.

gp.step("program.author", displayName=…, language=…, placement=…, …)

Create a Python or shader program with typed ports on a track, final frame, property, or composite. Use the property.author_controller step in project.edit to drive several property channels from one Python source.

Takes displayName, language, placement and optionally activeRange, contextResources, entrypoint, inputPorts, outputPorts, params, pythonPixels, resources, role, source, sourcePath.

gp.step("property.author_asset_swap", activeRange=…, displayName=…, frames=…, targetNodeId=…)

Animate discrete asset-reference changes.

Takes activeRange, displayName, frames, targetNodeId.

gp.step("property.author_controller", activeRange=…, bindings=…, displayName=…, entrypoint=…, params=…, source=…)

Drive several property channels from one Python source with declared bindings and controls. Use the program.author step in project.edit for one property output or a frame program.

Takes activeRange, bindings, displayName, entrypoint, params, source.

gp.step("property.author_path_follow", activeRange=…, easingToNext=…, sampleCount=…, sourcePathShapeNodeId=…, targetNodeId=…, …)

Bake path-follow motion to transform keyframes.

Takes activeRange, easingToNext, sampleCount, sourcePathShapeNodeId, targetNodeId and optionally orientToPath.

gp.step("property.compile_timing_chart", charts=…, effectNodeId=…, range=…)

Compile timing-chart events into animation. Content past the composition's duration is not in the output until its duration is set.

Takes charts, effectNodeId, range.

gp.step("property.drive", driver=…, target=…)

Set or clear a property's live driver by exact address. Use physics.author_simulation to bake motion into keys.

Takes driver, target.

gp.step("property.set", targets=…, …)

Set one or more property addresses to a static value or keyframe schedule.

Takes targets and optionally at, displayName, keyframes, keysFrom, value.

gp.step("property.set_dimensions", dimensions=…, ownerNodeId=…, propertyId=…, …)

Choose how Position, Scale or Anchor animates: whole, where each key holds x, y and z, or separate, where each axis has its own keys and timing. Rotation always keys each axis on its own. Separating keeps every key exactly. Joining axes keyed at different times needs resample: true. Returns commandId and revision.

Takes dimensions, ownerNodeId, propertyId and optionally resample.

gp.step("puppet.create", targetClip=…, time=…, …)

Create an editable deformation mesh on a visual clip, from its alpha or shape or from supplied topology. Mesh points and pins are pixels from the layer centre, x right and y down. A pin pulls the mesh from its rest position to its target position; rotation, overlap depth and rigidity are Params that the set or property.drive step in project.edit can set, key or drive. Read the mesh and its controls with project.query (fields mesh and params).

Takes targetClip, time and optionally cellSizePixels, mesh, pins, preserveRegions.

gp.step("puppet.update", effectNodeId=…, operation=…, …)

Add or remove a pin or a preserve region, turn on a pin's rotation or overlap depth, replace the mesh while keeping existing controls and their animation, or remove the puppet. Set or key pin values with the set step in project.edit; read them with project.query (fields mesh and params).

Takes effectNodeId, operation and optionally centerPropertyId, depth, mesh, pin, positionXPropertyId, region, rotation.

gp.step("python.delete_module", name=…)

Delete one project Python library or script by name.

Takes name.

gp.step("python.write_module", name=…, source=…, …)

Create or replace one project Python library or script from UTF-8 source; kind defaults to library.

Takes name, source and optionally kind.

gp.step("rig.create", activeRange=…, displayName=…, joints=…)

Create a semantic rig for parented visual clips.

Takes activeRange, displayName, joints.

gp.step("scene3d.layer_set", trackNodeId=…, …)

Enable or update a visual layer's 3D state, extrusion, and material. Extrusion distances use composition pixels. Use the set step in project.edit to edit its transform.

Takes trackNodeId and optionally ambient, baseColor, diffuse, enabled, extrusion, lit, metallic, roughness, specular, twoSided.

gp.step("set", on=…, props=…, …)

Set values, keys or object structure on one or more objects.

Takes on, props and optionally at, expect.

gp.step("storyboard.add_shots", name=…, shots=…, …)

Insert flat shots into the named storyboard in one undoable command. Omitted index appends; an index beyond the end also appends. Images reference imported image/video assets. Timing and notes are optional; fieldOrder sets note display order.

Takes name, shots and optionally index.

gp.step("storyboard.create", name=…, …)

Create an empty storyboard with a unique name and optional description and notes. Use fieldOrder to set their display order.

Takes name and optionally description, fieldOrder, fields.

gp.step("storyboard.delete", name=…)

Delete the named storyboard and its shots. Imported assets and timeline content remain independent.

Takes name.

gp.step("storyboard.delete_shot", name=…, shotId=…)

Delete one shot belonging to the named storyboard without deleting its media asset.

Takes name, shotId.

gp.step("storyboard.reorder_shots", name=…, shotIds=…)

Set the complete order of shots on one named storyboard. Include each shot id exactly once; other storyboards keep their order.

Takes name, shotIds.

gp.step("storyboard.update", name=…, …)

Rename or edit a storyboard by name while preserving its identity and shots. Omitted fields are preserved; null removes a note. Use fieldOrder to reorder notes.

Takes name and optionally description, fieldOrder, fields, newName.

gp.step("storyboard.update_shot", changes=…, name=…, shotId=…)

Patch one shot on the named storyboard. Preserve omitted fields; null clears imageAssetId or plannedRange, and removes a note. Use fieldOrder to reorder notes.

Takes changes, name, shotId.

gp.step("timeline.create_composition_clip", compositionNodeId=…, timelineRange=…, trackNodeId=…, …)

Place a composition on an existing visual track. Content past the composition's duration is not in the output until its duration is set.

Takes compositionNodeId, timelineRange, trackNodeId and optionally continuousRasterization, playbackRate, sourceRange, transform.

gp.step("timeline.create_shape_clip", geometry=…, kind=…, timelineRange=…, trackNodeId=…, …)

Create a native shape or bounded ordered heterogeneous shape group. Content past the composition's duration is not in the output until its duration is set.

Takes geometry, kind, timelineRange, trackNodeId and optionally members, style, transform.

gp.step("timeline.create_text_clip", text=…, timelineRange=…, trackNodeId=…, …)

Create native text. Content past the composition's duration is not in the output until its duration is set.

Takes text, timelineRange, trackNodeId and optionally runs, stagger, style, transform.

gp.step("timeline.edit", clipNodeIds=…, operation=…, …)

Rename, move, align, sequence, split, duplicate, insert, overwrite, trim, ripple, roll, slip, slide, stretch or close a gap on the timeline. set_range sets both timeline edges and can choose the played source interval. Move and move_start_to can change to a compatible track. Insert makes room; overwrite replaces the destination interval and keeps its outer tails. Copies keep their settings and their own animation. Use project.edit with the delete step for deletion without a ripple. Each call is one undo step and returns the clips it leaves selected, such as new copies or split halves.

Takes clipNodeIds, operation and optionally correctionPolicy, destinationTrackNodeId, gap, name, sourceRange, time, timelineRange.

gp.step("timeline.layers", operation=…, …)

Create, rename, order or set the compositing and presentation of a track. Use project.edit with the delete step to remove a track. Reordering stays within the visual or audio stack. The top visual track renders in front. New tracks start on top unless a place is given. Composition reads list tracks top to bottom. Each call is one undo step.

Takes operation and optionally aboveTrackNodeId, anchorTrackNodeId, belowTrackNodeId, blendMode, compositionNodeId, guide, kind, locked, mattes, name, shy, solo, trackNodeId, trackOrder, visible.

gp.step("timeline.place_media", assetId=…, …)

Place an imported media asset on a track. Choose its source interval, timeline interval, playback rate and visual crop. Place permits overlap; insert makes room; overwrite preserves outer tails. Video and audio can be placed together or separately. Returns the created clip and track IDs.

Takes assetId and optionally audioTrack, components, compositionNodeId, ifExists, mode, playbackRate, sourceRange, sourceRect, start, timelineRange, track.

gp.step("timeline.replace_clip_source", assetId=…, clipNodeId=…)

Replace only an asset-backed clip's media source while preserving authored state.

Takes assetId, clipNodeId.

gp.step("timeline.set_clip_parent", clipNodeId=…, parentTransformSource=…)

Parent a visual clip to another clip.

Takes clipNodeId, parentTransformSource.

gp.step("timeline.set_source_time", clipNodeId=…, keyframes=…)

Set or clear a clip's monotonic non-uniform source-time curve.

Takes clipNodeId, keyframes.

gp.step("timeline.update_clip_audio_mix", clipNodeId=…, …)

Change selected audio-mix fields.

Takes clipNodeId and optionally fadeInDuration, fadeOutDuration, level.

gp.step("timeline.update_clip_playback_rate", clipNodeId=…, playbackRate=…)

Change a clip playback rate.

Takes clipNodeId, playbackRate.

gp.step("timeline.update_clip_transform", clipNodeId=…, …)

Change selected transform fields on any visual clip.

Takes clipNodeId and optionally changes, continuousRasterization.

gp.step("timeline.update_shape_clip", clipNodeId=…, …)

Change selected shape fields while preserving every omitted field.

Takes clipNodeId and optionally geometry, kind, members, style, timelineRange, transform.

gp.step("timeline.update_text_clip", clipNodeId=…, …)

Patch text clip content, timing, and styling.

Takes clipNodeId and optionally runs, stagger, style, text, timelineRange, transform.

Tools

Each tool says where it runs. Tools that read the staged project run inside a script. The rest save on their own, so they're called as tool calls before or after a script, and a script reads their results.

gp.tool("analysis.align_transcript_to_audio", assetId=…, languageHint=…, transcriptScope=…, transcriptSource=…, …)

Aligns supplied transcript text to an asset's audio. Accepts inline text or text selected from a conversation message. Returns word timing and confidence in source-asset seconds, unresolved spans, and an alignment artifact.

Takes assetId, languageHint, transcriptScope, transcriptSource and optionally end, start.

As a tool call, before or after a script.

gp.tool("analysis.audio_structure", end=…, start=…, …)

Measures beats, downbeats, sections, onsets, vocal pitch, and RMS loudness over a composition audio range. Select features to limit work and returned data. Beat times are aligned to acoustic attacks; confidence reflects both model and audio evidence. Returns a compact result and a cataloged artifact for detailed measurements.

Takes end, start and optionally alignedWords, features.

As a tool call, before or after a script.

gp.tool("analysis.detect_color_regions", compositionNodeId=…, dominance=…, end=…, includeRegions=…, maxRegions=…, minChannel=…, minRegionCoverage=…, resolution=…, samples=…, start=…, targetColor=…)

Measure red, green or blue regions in rendered frames. Returns matched pixel coverage as a fraction from 0 to 1 and aggregate and largest-region bounds in composition pixels from the top left, with x right and y down.

Takes compositionNodeId, dominance, end, includeRegions, maxRegions, minChannel, minRegionCoverage, resolution, samples, start, targetColor.

As a tool call, before or after a script.

gp.tool("analysis.detect_motion_regions", differenceThreshold=…, end=…, maxRegions=…, minRegionCoverage=…, resolution=…, samples=…, start=…, …)

Measure changed regions across rendered frames. Returns changed coverage as a fraction from 0 to 1 and region bounds, centers, widths and heights in composition pixels from the top left, with x right and y down, plus a preview and an artifact selector for more comparisons.

Takes differenceThreshold, end, maxRegions, minRegionCoverage, resolution, samples, start and optionally compositionNodeId.

As a tool call, before or after a script.

gp.tool("analysis.edit_transcript", artifactId=…, expectedDocumentHash=…, operation=…, …)

Correct a transcript's text, timing or speaker.

Takes artifactId, expectedDocumentHash, operation and optionally boundary, byteOffset, correctionId, first, name, second, span, speaker, text, timing.

As a tool call, before or after a script.

gp.tool("analysis.follow_tracked_region", target=…, trackArtifactId=…, …)

Makes a property follow a tracked region centre with ordinary keyframes. Takes a candidate track artifact and a property.read address, and returns the bake id, offset and keyed range. Supply bakeNodeId with another track to replace this bake's keys.

Takes target, trackArtifactId and optionally acknowledgeDivergedOverwrite, axis, bakeNodeId, offset, range.

As a tool call, before or after a script.

gp.tool("analysis.get_artifact", artifactId=…, …)

Retrieves a bounded view of a cataloged analysis artifact by id. JSON findings can be narrowed with an RFC 6901 jsonPointer of at most 2048 UTF-8 bytes, resolved directly against the raw artifact document root. Automatically externalized large tool results store the original tool-result object at that root; they do not add a /payload envelope. An omitted or empty pointer returns the full root only when it fits; otherwise it returns a compact manifest with bounded, exact selectable availableJsonPointers and reports when that pointer metadata is truncated. Use listed names directly and never invent a /payload prefix. Timed words are typed by artifact kind: use /alignment/words for transcript_alignment, /transcript/words for transcript, and /alignedWords/words for audio_structure; do not guess /words at the root. offset/limit pages whole array elements. An oversized string returns raw UTF-8 text chunks; an oversized object, scalar, or array element returns serialized JSON UTF-8 chunks. Each returned cursor is bound to the artifact contentHash and normalized artifactId/jsonPointer selection; pass it back unchanged, and start a fresh read if either changes. Continue only with the exact returned cursor and concatenate utf8_json fragments before parsing. Every response is capped below 8192 bytes (8 KiB). An oversized root-manifest error identifies the artifact, byte count, budget, and bounded known jsonPointers when available. Image artifacts return metadata only so image context stays on the capability-gated image tools.

Takes artifactId and optionally cursor, jsonPointer, limit, offset.

Inside a script.

gp.tool("analysis.get_asset_contact_sheet", assetId=…, …)

Decodes source frames from an imported image or video asset into a contact-sheet image. Transparent pixels appear over a checkerboard. A still image has one frame at t=0. A video needs sourceRange and sampleTimes. Set resolution for the maximum size of each frame; the sheet uses one cell per sample and keeps the source aspect ratio.

Takes assetId and optionally columns, resolution, rows, sampleTimes, sourceRange.

As a tool call, before or after a script.

gp.tool("analysis.get_render_contact_sheet", end=…, …)

Renders graph frames over [start,end), tiles them into one PNG, and returns it for visual inspection. With overlay none, each default frame shows the export view over black. Set preserveAlpha true to inspect transparency. Supply sampleTimes for exact frames or sampleCount for evenly spaced coverage. Omit columns and rows for a compact layout. tileResolution defaults to 320 by 180 per tile. For one full-resolution still use analysis.get_render_frame_image.

Takes end and optionally columns, compositionNodeId, overlay, preserveAlpha, rows, sampleCount, sampleTimes, start, tileResolution.

As a tool call, before or after a script.

gp.tool("analysis.get_render_frame_image", time=…, …)

Render a frame as a PNG. Set preserveAlpha to inspect transparency. Omit compositionNodeId for the project output composition. crop selects a rectangle in rendered pixels from the top left after rendering.

Takes time and optionally compositionNodeId, crop, overlay, preserveAlpha, resolution.

As a tool call, before or after a script.

gp.tool("analysis.get_render_frame_images", sampleTimes=…, …)

Renders graph frames at selected times and returns a separate PNG for each time. With overlay none, each default PNG shows the export view over black. Set preserveAlpha true to inspect transparency. Omit compositionNodeId for the project output composition.

Takes sampleTimes and optionally compositionNodeId, overlay, preserveAlpha, resolution.

As a tool call, before or after a script.

gp.tool("analysis.inspect_render_frame", items=…, …)

Measure one to sixteen rendered frames. Returns pixel counts, normalized coverage, luma and channel values from 0 to 1, non-transparent and non-black bounds in composition pixels, and evenly spaced RGBA samples in rendered-pixel coordinates. Put every request in items; omit compositionNodeId for the project output composition.

Takes items and optionally compositionNodeId, pixelProbeGridSize, resolution.

As a tool call, before or after a script.

gp.tool("analysis.list_artifacts", …)

List analysis artifacts with ids, kinds and frame details. Set detail to full for paths, hashes and provenance. Use nextCursor with the same filters, limit and detail for another page.

Optionally takes cursor, detail, kind, limit, runId.

As a tool call, before or after a script.

gp.tool("analysis.list_fonts", …)

Lists font families with their style names and default style from the resolver text rendering uses; pass a family as fontFamily and a style as fontStyle on text clips.

Optionally takes family, familyPrefix, limit, offset, sourceVersion.

As a tool call, before or after a script.

gp.tool("analysis.measure_motion_cadence", end=…, start=…, …)

Measure motion and stillness across rendered frames. Returns normalized frame-change magnitude and coverage, global translation in frame widths and heights, speed in frame lengths per second, acceleration and jerk, near-static durations in seconds, and offsets from reference times in seconds.

Takes end, start and optionally compositionNodeId, differenceThreshold, nearStaticChangeThreshold, phaseResponseFloor, referenceTimes, resolution, samplesPerSecond, topEventCount.

As a tool call, before or after a script.

gp.tool("analysis.observe_asset_segment", assetId=…, frameRate=…, question=…, sourceRange=…, …)

Answers a visual question about a source-media segment without placing it. Returns a bounded UTF-8 preview and exact metadata for the complete observation and source-video artifacts.

Takes assetId, frameRate, question, sourceRange and optionally resolution.

As a tool call, before or after a script.

gp.tool("analysis.observe_color_scopes", time=…, …)

Measure every non-transparent pixel in one final rendered frame. Returns average RGBA, 16-bin RGB and luma histograms, luma percentiles, and exact encoded channel fractions at code values 0 and 255; values are normalized from 0 to 1 in rendered sRGB RGBA8 space. Also returns histogram, luma-waveform and RGB-parade dimensions in rendered pixels and a PNG overlay.

Takes time and optionally compositionNodeId.

As a tool call, before or after a script.

gp.tool("analysis.observe_image_structure", time=…, …)

Measure structure in one rendered frame. Returns normalized coherence, energy, edge coverage and quietness, dominant line orientation in degrees, normalized luma and clipping fractions, and title-box candidates in composition pixels from the top left, plus an overlay image.

Takes time and optionally compositionNodeId, foregroundMaskArtifactId, titleBox.

As a tool call, before or after a script.

gp.tool("analysis.observe_optical_flow", fromTime=…, toTime=…, …)

Measure dense optical flow between two rendered frames. Returns vector components and flow magnitudes in rendered pixels, active and reliable coverage as fractions from 0 to 1, consistency error in rendered pixels, plus a vector Field artifact and PNG overlay.

Takes fromTime, toTime and optionally compositionNodeId.

As a tool call, before or after a script.

gp.tool("analysis.observe_shape_geometry", channel=…, polarity=…, source=…, threshold=…, …)

Measure signed distance and contours in a rendered frame or cataloged image. Returns signed-distance statistics in rendered pixels, foreground coverage as a fraction from 0 to 1, contour points and fitted shape geometry in rendered pixels, plus a scalar Field artifact and PNG overlay.

Takes channel, polarity, source, threshold and optionally maximumContours.

As a tool call, before or after a script.

gp.tool("analysis.observe_temporal_statistics", sampleTimes=…, …)

Measure change across rendered frames at exact composition times in seconds. Returns normalized per-pixel variance and change, exposure drift, flicker and cut likelihood as fractions from 0 to 1, and active-region bounds in composition pixels, plus a variance/change PNG overlay.

Takes sampleTimes and optionally compositionNodeId.

As a tool call, before or after a script.

gp.tool("analysis.read_transcript", artifactId=…, …)

Reads the resolved transcript correction, preserving stable span identities, exact UTF-8 text, timing roles, source asset identity, and the captured project revision. Optional query searches exact text in source order.

Takes artifactId and optionally correctionId, limit, offset, query, spanId, textEndByte, textStartByte.

As a tool call, before or after a script.

gp.tool("analysis.resolve_source_clock", frameRate=…, source=…, times=…, …)

Resolve sample times to the source frames displayed by the renderer, including clip trims, reverse, holds, and retiming. For clip sources, times and range use composition time; a sample must display the selected clip. For assets they use media time; for compositions they use composition time. Returns range as requested when supplied and sourceRange covering the source frames displayed at composition or media frames inside that range, plus source, resolved sample times, frame rate, native source dimensions, and the capture basis for a mask canvas. Source positions and sizes use source pixels from the top left, y down. frameRate supplies the editing cadence for still images; a composition source requires range.

Takes frameRate, source, times and optionally range.

As a tool call, before or after a script.

gp.tool("analysis.segment_subject", downsampleRatio=…, end=…, frameRate=…, source=…, start=…)

Create a reusable person matte sequence from the requested range.

Takes downsampleRatio, end, frameRate, source, start.

As a tool call, before or after a script.

gp.tool("analysis.track_region", compositionNodeId=…, end=…, maxSearchRadius=…, minMatchScore=…, region=…, resolution=…, samples=…, start=…)

Track a selected rectangle across rendered frames. The rectangle and search radius use composition pixels from the top left, with x right and y down. Returns a candidate region track for inspection.

Takes compositionNodeId, end, maxSearchRadius, minMatchScore, region, resolution, samples, start.

As a tool call, before or after a script.

gp.tool("analysis.transcribe_media", assetId=…, …)

Transcribes an audio or video asset, optionally over a requested range in source-asset seconds, and catalogs a lossless JSON transcript artifact with segment, sentence, and word timings plus confidence quality metadata. The immediate response is a bounded fields-plus-rows projection with exact typed retrieval pointers; raw evidence stays in the artifact. Results are reused from the project package cache by asset and source range. This is evidence for later graph changes and does not mutate the graph.

Takes assetId and optionally end, languageHint, prompt, start.

As a tool call, before or after a script.

gp.tool("analysis.view_local_image", path=…)

Show an image file without importing it. The file must be inside the project folder, its workbench, or your scratchpad when you have one; an absolute path is safest.

Takes path.

As a tool call, before or after a script.

gp.tool("asset.inspect", assetId=…, …)

Read one asset's metadata, name and paths.

Takes assetId and optionally field, revision, sourceVersion, textLimit, textOffset.

As a tool call, before or after a script.

gp.tool("asset.list", …)

List project assets with names and metadata.

Optionally takes limit, offset, revision, sourceVersion.

As a tool call, before or after a script.

gp.tool("assets.derive_alpha_image", artifactId=…, featherPixels=…, invert=…, name=…, …)

Uses the exact asset identity and source-media time recorded by a cataloged image/png mask_frame, decodes that source frame from the task's pinned publication, applies the mask to existing source alpha, gives semi-transparent edge pixels the subject's own colour, and imports one ordinary reusable PNG asset with derivation provenance. Composition-derived, untyped, overlay, and mask-sequence artifacts are rejected. Pixel decoding, mask processing, and PNG encoding run from a captured workspace snapshot without holding the workspace lock.

Takes artifactId, featherPixels, invert, name and optionally role, threshold, time.

As a tool call, before or after a script.

gp.tool("assets.import_kit", manifestPath=…)

Imports every image, video, or audio entry in a kit manifest's assets array through the normal workspace media path, preserving free-form role tags and applying the optional style_spec to the canonical project style spec.

Takes manifestPath.

As a tool call, before or after a script.

gp.tool("composition.inspect", …)

Read compositions, tracks and items. Duration is seconds, or null when it follows content.

Optionally takes collection, compositionNodeId, innerCollection, innerLimit, innerOffset, itemNodeId, limit, memberIndex, nameLimit, nameOffset, offset, revision, sourceVersion, textField, textLimit, textOffset, trackNodeId.

As a tool call, before or after a script.

gp.tool("control.inspect_surface", compositionId=…, …)

Read a composition's complete published surface and inspect exact requested property sources. Publications report source labels, capabilities, unavailable reasons and reveal targets; requested sources also report eligible value editors and canonical scalar schedules with exact key IDs and times.

Takes compositionId and optionally sources.

As a tool call, before or after a script.

gp.tool("effect.inspect_builtin", builtin=…)

Read a builtin effect's accepted fields and units.

Takes builtin.

As a tool call, before or after a script.

gp.tool("effect.inspect_graphs", …)

Read effect graphs, or one graph's targets, bindings, nodes and edges.

Optionally takes collection, fields, graphId, limit, offset, revision, sourceVersion.

As a tool call, before or after a script.

gp.tool("export.render_video", end=…, outputPath=…, start=…, …)

Starts a video export job. The video result is delivered when it finishes. Use job.list and job.wait to follow it, or job.cancel to stop it. Omit compositionNodeId to export the project output composition or provide it to export that composition. Always uses the selected composition's native output resolution. Omit frameRate to use the composition frame rate, and omit codec to use the native product default. prores_4444 keeps the composition's transparency, for a cutout to reuse in another project or editor, and needs a .mov outputPath; the other codecs write .mp4 over black. colorOutput selects sdr, pq, or hlg; HDR requires HEVC. outputPath supplies a filename hint only; every export is published inside the project-owned exports directory. Success returns the cataloged video artifact handle and exact frame counts. Supported codecs: h264, hevc, mjpeg, mp4v, prores_4444.

Takes end, outputPath, start and optionally codec, colorOutput, compositionNodeId, frameRate.

As a tool call, before or after a script.

gp.tool("history.create_checkpoint", name=…, …)

Name a History position. Omit commandId to mark the current applied step. Creating a checkpoint does not change the edit or add an undo step.

Takes name and optionally commandId.

As a tool call, before or after a script.

gp.tool("history.list_checkpoints")

List the project's named History checkpoints, including their IDs, command positions, and creation times.

Takes no arguments.

As a tool call, before or after a script.

gp.tool("history.rename_checkpoint", checkpointId=…, name=…)

Rename a History checkpoint by its checkpointId from history.list_checkpoints.

Takes checkpointId, name.

As a tool call, before or after a script.

gp.tool("history.undo", …)

Undo an earlier change or run, redo an undone change, return a composition to a History point, or restore the whole project to a point. Use commandId for a change, runId for a run, compositionId with toCommandId for a composition, or restoreToCommandId for the project. When a preview needs confirmation, call again with its confirmToken.

Optionally takes commandId, compositionId, confirmToken, redo, restoreToCommandId, runId, toCommandId.

As a tool call, before or after a script.

gp.tool("job.cancel", job=…, …)

Request cancellation of owned work and read its current status.

Takes job and optionally include, mode.

As a tool call, before or after a script.

gp.tool("job.list", …)

List this run's active jobs, optionally including retained finished jobs or jobs for objects.

Optionally takes cursor, expect, include, includeCompleted, of.

As a tool call, before or after a script.

gp.tool("job.wait", job=…, …)

Wait for a job to change state, or read its status and available result.

Takes job and optionally after, include, timeout.

As a tool call, before or after a script.

gp.tool("mask.attach", artifactId=…, clipNodeId=…, mode=…, name=…, …)

Attach a saved object mask to a footage clip.

Takes artifactId, clipNodeId, mode, name and optionally cutOut.

As a tool call, before or after a script.

gp.tool("mask.scope_effect", effectNodeId=…, outputScope=…, region=…, …)

Apply a stack effect inside or outside a clip mask, or to the whole image.

Takes effectNodeId, outputScope, region and optionally clipNodeId, maskSourceNodeId.

As a tool call, before or after a script.

gp.tool("measure", measure=…)

Measure evaluated motion, rendered pixels, output audio or analysis results.

Takes measure.

Inside a script.

gp.tool("models.catalogue", connectionId=…, …)

Reads one page of the provider's live model catalogue using a saved connection. Pass nextPageToken as pageToken to continue; an empty token ends the list. An empty kinds array means the model's capabilities are unknown. Check the model's documentation before relying on its operation support.

Takes connectionId and optionally pageSize, pageToken.

As a tool call, before or after a script.

gp.tool("models.list", …)

Lists configured models and available local roto capabilities. Configured models include their operations, defaults, providers, and saved connections. Use stroke_canvas for prompted roto and analysis.segment_subject for automatic person matting. Supply operationType to filter the list.

Optionally takes operationType.

As a tool call, before or after a script.

gp.tool("models.retry_operation_publication", modelRunId=…)

Publishes validated output already preserved by a model-operation run. This never contacts or reruns the provider and is independent of current model availability.

Takes modelRunId.

As a tool call, before or after a script.

gp.tool("models.run_operation", configuredModelId=…, inputs=…, operationType=…, …)

Runs one exact interactive operation from models.list with role-addressed typed inputs. The model's provider script executes it; Grapple validates the inputs against the declaration and verifies, persists and publishes the declared outputs. Use it to test a newly configured model with one small real request.

Takes configuredModelId, inputs, operationType and optionally activationSelections, runLabel.

As a tool call, before or after a script.

gp.tool("note.list", …)

List project notes with title and markdown previews.

Optionally takes limit, offset, revision, sourceVersion.

As a tool call, before or after a script.

gp.tool("note.read", noteNodeId=…, …)

Read one note's title, markdown and attachment.

Takes noteNodeId and optionally field, revision, sourceVersion, textLimit, textOffset.

As a tool call, before or after a script.

gp.tool("particle.inspect", effectNodeId=…, …)

Read particles at a time. Positions use composition pixels from the top left, velocity uses pixels per second with positive y down, and opacity uses percent.

Takes effectNodeId and optionally limit, offset, time.

As a tool call, before or after a script.

gp.tool("physics.author_box2d_simulation", acknowledgeDivergedOverwrite=…, bodies=…, displayName=…, gravity=…, range=…, rebake=…, seed=…, solver=…, timestep=…, …)

Bake a Box2D scene into property keys. Use writable channel addresses from project.query's targetProperties field. Positions and collider geometry use composition pixels from the top left, y down; linear velocity uses pixels per second and gravity uses pixels per second squared. Set rebake and simulationNodeId to replace a bake. Returns commandId, simulationNodeId, artifactId, collisionEventCount, and solve stats. This tool accepts bodies and colliders.

Takes acknowledgeDivergedOverwrite, bodies, displayName, gravity, range, rebake, seed, solver, timestep and optionally simulationNodeId.

As a tool call, before or after a script.

gp.tool("physics.author_simulation", acknowledgeDivergedOverwrite=…, displayName=…, range=…, rebake=…, seed=…, solver=…, timestep=…, …)

Bake spring, follower, or pendulum motion into property keys. Use the property.drive step in project.edit for motion that stays live. Use writable channel addresses from project.query's targetProperties field. Position values and lengths use composition pixels from the top left, y down; rotation uses degrees. Set rebake and simulationNodeId to replace a bake. Returns commandId, simulationNodeId, artifactId, collisionEventCount, and solve stats.

Takes acknowledgeDivergedOverwrite, displayName, range, rebake, seed, solver, timestep and optionally simulationNodeId.

As a tool call, before or after a script.

gp.tool("preview.get_frame_info", items=…, …)

Renders one to sixteen preview frames and returns render and pixel diagnostics. Put even a single request in items. Omit compositionNodeId for the project output composition. imageRendered means an image was allocated; pixelStats.hasVisiblePixels means at least one pixel has nonzero alpha, including black artwork. It does not measure contrast against the black export background. nonBlackPixels counts coloured pixels. Use analysis.get_render_frame_image, analysis.get_render_frame_images, or analysis.get_render_contact_sheet to view the export background. The result contains count and a frames array.

Takes items and optionally compositionNodeId.

As a tool call, before or after a script.

gp.tool("program.author_signal", attackSeconds=…, highFrequencyHz=…, inputCeilingDbfs=…, inputFloorDbfs=…, lowFrequencyHz=…, measurement=…, outputMaximum=…, outputMinimum=…, range=…, releaseSeconds=…, samplesPerSecond=…, source=…, target=…, windowSeconds=…, …)

Measures audio level or a frequency band from the composition mix or an audio artifact and keys one animatable property component. Specify its property.read address and output range in that property's displayed units. Returns the program, artifact, and target addresses.

Takes attackSeconds, highFrequencyHz, inputCeilingDbfs, inputFloorDbfs, lowFrequencyHz, measurement, outputMaximum, outputMinimum, range, releaseSeconds, samplesPerSecond, source, target, windowSeconds and optionally displayName.

As a tool call, before or after a script.

gp.tool("program.inspect_signal", programNodeId=…)

Returns the audio source, target property and component, measurement controls, baked artifact, divergence state, and up to 32 signal preview samples.

Takes programNodeId.

As a tool call, before or after a script.

gp.tool("program.rebake_signal", acknowledgeDivergedOverwrite=…, attackSeconds=…, highFrequencyHz=…, inputCeilingDbfs=…, inputFloorDbfs=…, lowFrequencyHz=…, measurement=…, outputMaximum=…, outputMinimum=…, programNodeId=…, range=…, releaseSeconds=…, samplesPerSecond=…, windowSeconds=…)

Measures audio again with the supplied controls and replaces only this program's baked property keys. Set acknowledgeDivergedOverwrite when its keys have been edited. Returns the updated program and artifact addresses.

Takes acknowledgeDivergedOverwrite, attackSeconds, highFrequencyHz, inputCeilingDbfs, inputFloorDbfs, lowFrequencyHz, measurement, outputMaximum, outputMinimum, programNodeId, range, releaseSeconds, samplesPerSecond, windowSeconds.

As a tool call, before or after a script.

gp.tool("project.edit", label=…, mode=…, steps=…, …)

Apply ordered project edits as one History row. A later step uses an earlier step's result by writing "$name" or "$name.field" for a step named with as; every reference-shaped token is reserved, including undeclared names. Write "$$name" for literal "$name". The full receipt retains ordered stepResults, including unnamed steps; results holds the outputs named with as.

Takes label, mode, steps and optionally ifRevision.

Inside a script, staged with its edits.

gp.tool("project.inspect", …)

Read project structure and counts. A composition's duration is seconds, or null when it follows its content.

Optionally takes collection, limit, offset, revision, sourceVersion.

As a tool call, before or after a script.

gp.tool("project.query", fields=…, select=…, …)

Query project objects and return selected fields as a table.

Takes fields, select and optionally at, explain, limit, offset, revision, sourceVersion, view.

Inside a script.

gp.tool("project.read_style", styleField=…, …)

Read one project style value.

Takes styleField and optionally revision, sourceVersion, styleIndex, textLimit, textOffset.

As a tool call, before or after a script.

gp.tool("project.refresh")

Advances your pinned project view to one current canonical publication. Ordinary reads remain pinned until this tool, your own commit, or an agent.wait join advances them.

Takes no arguments.

As a tool call, before or after a script.

gp.tool("property.read", ownerNodeId=…, …)

Read a property's authored value, units, keys and driver, or list an object's properties.

Takes ownerNodeId and optionally componentIndex, includeKeys, propertyId, time.

As a tool call, before or after a script.

gp.tool("providers.read", providerId=…)

Returns one installed provider's manifest and, for a Python provider, its complete script. Shipped providers are the working reference for writing a new one.

Takes providerId.

As a tool call, before or after a script.

gp.tool("python.list_modules")

List project Python libraries and scripts with kinds and source hashes. Only libraries import as grapple_project.<name>.

Takes no arguments.

As a tool call, before or after a script.

gp.tool("python.read_module", name=…)

Read the exact source, kind and hash of one project Python source.

Takes name.

As a tool call, before or after a script.

gp.tool("python.run_script", label=…, mode=…, …)

Run Python by source or saved script name with a label and mode (apply or dry_run). The module's top level runs with gp and ctx bound; run(ctx) is called afterwards when defined. Every project edit stages and commits once at the end as one undo step. Use gp.step for registered edit steps and gp.tool for candidate-safe reads. Call imports, analyses, solves, exports and preview videos as separate tools. Use help, gp.search and gp.examples for discovery; read grapple://python/stubs for the generated API stub. Scripts can read the project and write files in the Workbench and the chat's scratchpad; they have no network access. Files written by the script are not undone. Values are number, bool, str, gp.Vec2, gp.Vec3, gp.Rect and gp.Path. Coordinates use composition pixels with x right and y down. A clip's frame is centred on the composition: pivot is a point on that frame and position is where it lands. Local shape and text points are pixels from their frame's centre; rotation uses degrees, and scale and opacity use percent. Use gp.ease, gp.remap, gp.loop, gp.wiggle and gp.memo for numeric work. Project libraries import as grapple_project.<name>; project scripts run by name.

Takes label, mode and optionally name, source, workingDirectory.

As a tool call, before or after a script.

gp.tool("render", of=…, render=…, …)

Render frames, a labelled contact sheet, a preview video or the output audio through the selected view.

Takes of, render and optionally compare, include, overlay, view.

Inside a script for frames, sheets and audio; a video is a tool call.

gp.tool("render_plan.inspect", …)

Read a composition's render plan and its item collections.

Optionally takes collection, compositionNodeId, effectGraphId, itemNodeId, limit, offset, revision, sourceVersion, textClipNodeId, textLimit, textOffset.

As a tool call, before or after a script.

gp.tool("resources.read", uri=…)

Read one tool, edit-step or Python API contract by its grapple:// resource URI.

Takes uri.

As a tool call, before or after a script.

gp.tool("rig.inspect", rigEffectNodeId=…, …)

Inspect bounded rig joints, controls, or a selected control keyframe page.

Takes rigEffectNodeId and optionally controlId, jointId, limit, offset.

As a tool call, before or after a script.

gp.tool("runtime.inspect_diagnostics", …)

Read runtime diagnostics or one exact diagnostic.

Optionally takes codeLimit, codeOffset, diagnosticIndex, limit, messageLimit, messageOffset, offset, revision, sourceLimit, sourceOffset, sourceVersion.

As a tool call, before or after a script.

gp.tool("scene3d.inspect", nodeId=…)

Read a layer, clip, camera or composition's 3D settings: whether 3D is on, a layer's material and extrusion, a camera's lens, projection and planes, and a composition's lights. Distances are in composition pixels. Read position, rotation, scale and anchor with property.read.

Takes nodeId.

As a tool call, before or after a script.

gp.tool("solve.camera", solves=…)

Reconstruct a footage camera/lens and report error and depth.

Takes solves.

As a tool call, before or after a script.

gp.tool("storyboard.inspect", name=…, …)

Read a storyboard by exact name, including ordered shots, optional images, timing and notes. Each fieldOrder lists note names in display order. Optional offset/limit page the shots; continue using the returned nextRead.

Takes name and optionally limit, offset, revision, sourceVersion.

Inside a script.

gp.tool("storyboard.list")

List named storyboards in the project and their shot counts.

Takes no arguments.

Inside a script.

gp.tool("stroke_canvas.apply", strokes=…, viewArtifactId=…, …)

Applies ordered paint or mask strokes to a canvas view. Points and path controls use source canvas pixels from the top left. Paint colours use x, y, z from 0 to 1; opacity is percent. Point and box prompts select a subject. Returns a replacement viewArtifactId for the next apply or finalize call.

Takes strokes, viewArtifactId and optionally granularityIndex.

As a tool call, before or after a script.

gp.tool("stroke_canvas.create", base=…, output=…)

Creates a mask or paint canvas from a blank image, a media frame, or an image artifact. Blank colours use x, y, z from 0 to 1 and opacity in percent. Returns a documentArtifactId to pass to stroke_canvas.observe.

Takes base, output.

As a tool call, before or after a script.

gp.tool("stroke_canvas.finalize", output=…, viewArtifactId=…)

Publishes the canvas in viewArtifactId. Use the replacement view from the latest apply call. Paint publishes an image and can import it as an asset. mask_raster publishes authored alpha as a held mask sequence. mask tracks a prompted subject across the requested source frame range; use analysis.resolve_source_clock for clip time mapping. Returns the published artifact IDs and propagation status.

Takes output, viewArtifactId.

As a tool call, before or after a script.

gp.tool("stroke_canvas.observe", documentArtifactId=…, grid=…, maskOverlayOpacity=…, previewResolution=…, …)

Previews a canvas document and returns a viewArtifactId for apply or finalize. Stroke points use source canvas pixels from the top left. Point and box prompts select a subject; granularityIndex selects a candidate when needed.

Takes documentArtifactId, grid, maskOverlayOpacity, previewResolution and optionally granularityIndex, viewport.

As a tool call, before or after a script.

gp.tool("timeline.inspect_range", end=…, start=…, …)

Read counts or one collection of items in a timeline range.

Takes end, start and optionally collection, compositionNodeId, innerCollection, innerLimit, innerOffset, itemNodeId, limit, offset, revision, sourceVersion, textClipNodeId, textField, textLimit, textOffset.

As a tool call, before or after a script.

gp.tool("tools.inspect", digest=…, …)

Reads an installed plugin's manifest, surfaces, actions, and source files without changing it.

Takes digest and optionally sourcePath.

As a tool call, before or after a script.

gp.tool("tools.install", path=…)

Installs a validated plugin package folder from the current project's Workbench into the local plugin library.

Takes path.

As a tool call, before or after a script.

gp.tool("tools.list", …)

Searches installed plugins and returns their exact digests and package details.

Optionally takes query.

As a tool call, before or after a script.

gp.tool("tools.validate", path=…)

Validates a plugin package folder in the current project's Workbench, including its UI contract and Python syntax.

Takes path.

As a tool call, before or after a script.

gp.tool("vault.list")

Lists the secrets the user saved in the vault: each one's id, name, the hosts its value may be sent to, and the connections that send it. Values are never returned. Only the user adds secrets or changes their hosts, in Settings > Vault.

Takes no arguments.

As a tool call, before or after a script.

gp.tool("workspace.import_media", name=…, path=…, …)

Imports a local media file under a required person-readable name and registers it in the project asset catalog.

Takes name, path and optionally role.

As a tool call, before or after a script.