fp
v0.2.6Lodash-style value transforms (fp::get/pick/take/…) and fp::pipe — worker-side pipelines that move big values function→function without routing them through the model.
- macOS: arm64 · x64
- Linux: arm64 · armv7 · x64
- Windows: arm64 · x64 · x86
exact versions are immutable; binary and bundle artifacts are digest-pinned.
full markdown
/workers/fp.md. paste it into an llm prompt or pipe it through curl from a worker.install
dependencies
readme
fp
Lodash-style value transforms and fp::pipe — worker-side pipelines that
move big values function→function over the iii bus without routing them
through the model.
Install
iii worker add fpThe worker is a deploy: binary Rust worker with no configuration.
Why
An agent that fetches a document and re-types it into the next call's
arguments burns its context window and stalls the provider stream mid-call.
fp::pipe runs the whole move in one call — each step triggers a function
and its result lands in the next step's payload — so the value flows
worker→worker and the chat only ever sees per-step sizes and a preview.
While the worker is connected it also injects a usage section into the agent
system prompt via the harness pre-generate hook (fp::inject-guidance),
so the guidance is presence-gated: no fp worker, no prompt text. The
binding is one-shot at startup and relies on the engine's recoverable
triggers (iii #1962): bound before the harness is up, it parks as a pending
intent and activates when the harness registers the trigger type.
Functions
| function | lodash | request |
|---|---|---|
fp::pipe |
— | { through: [{function, payload?, into?}], preview_chars? } |
fp::get |
_.get |
{ value, path } — JSON pointer; a miss names the available keys |
fp::pick |
_.pick |
{ value, paths } — subset an object by top-level keys |
fp::omit |
_.omit |
{ value, paths } — drop top-level keys |
fp::take |
_.take |
{ value, n } — first n array elements / string chars |
fp::drop |
_.drop |
{ value, n } — skip the first n |
fp::map |
_.map |
{ value, path } — pluck a pointer from each element; misses → null |
fp::filter |
_.filter |
{ value, matches } — keep elements matching a partial object |
fp::split |
_.split |
{ value, separator } |
fp::join |
_.join |
{ value, separator? } (default ,) |
fp::uniq |
_.uniq |
{ value } — dedupe, first occurrences win |
fp::size |
_.size |
{ value } — array length / string chars / object key count |
fp::compact |
_.compact |
{ value } — remove null elements (0/false/"" are kept) |
fp::nth |
_.nth |
{ value, n } — element at index; negative counts from the end |
fp::getOr |
fp.getOr |
{ value, path, default } — pointer value, or default on a miss |
fp::flatten |
_.flatten |
{ value } — unnest one level |
fp::sortBy |
_.sortBy |
{ value, path } — stable ascending sort by a plucked pointer ("" = the element itself) |
fp::reverse |
_.reverse |
{ value } — reversed copy (immutable, fp-style) |
fp::sum |
_.sum / _.sumBy |
{ value, path? } — total; path plucks the addend from each element. Empty array totals 0 |
fp::mean |
_.mean / _.meanBy |
{ value, path? } — arithmetic mean; an empty array errors |
fp::min |
_.min / _.minBy |
{ value, path? } — smallest NUMBER (not the element holding it); an empty array errors |
fp::max |
_.max / _.maxBy |
{ value, path? } — largest NUMBER (not the element holding it); an empty array errors |
fp::groupBy |
_.groupBy |
{ value, path } — { key: [elements] } bucketed by a plucked key ("" = the element itself) |
fp::countBy |
_.countBy |
{ value, path } — { key: count } with the same key rules |
The reductions are the worker's arithmetic: all-integer inputs fold to an
integer (so the result compares cleanly in a fp::when guard), a non-numeric
element errors instead of being skipped — a skipped row is a total that
silently covers less than the caller counted — and min/max return the
number rather than lodash's …By element so a guard can compare it directly.
groupBy/countBy add the per-key axis the reductions lack: countBy gives
counts in one step, and groupBy's buckets each feed fp::sum for a per-key
total. Group keys must be a string, number, or boolean — lodash coerces a null
or object key to "null"/"[object Object]", silently merging distinct groups
into one bucket, so those error here instead.
Transforms take their input at value and return { value }. They deviate
from lodash where silence would thread garbage through a pipe: a type
mismatch is an error (not a silent {}); a map path matching NO element
errors naming what was available (pointers pluck stored fields — no computed
properties like /length; fp::size counts); compact removes only null
(lodash's full-falsey removal would eat legitimate 0/false/"");
nth out of bounds and size on a non-collection error instead of returning
undefined/0; sortBy requires every element to carry the key with one
comparable type. Sporadic map misses still become null, like lodash —
fp::compact drops them.
The pipe
fp::pipe { through: [
{ function: "scrapling::fetch",
payload: { url: "https://…", format: "markdown", main_content_only: true } },
{ function: "fp::get", payload: { path: "/content" } },
{ function: "fp::take", payload: { n: 20000 } },
{ function: "state::set", payload: { scope: "research", key: "article" } }
]}- Step N's result lands in step N+1's payload at
into(a JSON pointer, default/value— which is exactly where the transforms andstate::setread their input, so most pipes need nointoat all). - A transform step threads the transformed value itself — the
{ value }wrapper in the function table is only its direct-call response shape, so there is never a{value}layer to unwrap between steps. - The FIRST step receives no threaded value: start with a producing function
(a fetch, a
state::get) or seed a leading transform viapayload.value. An unseeded leading transform fails validation before anything runs. fp::*transform steps run inline in this worker; every other step is one bus trigger with a 120 s budget. The whole pipe must also fit the caller's own dispatch timeout onfp::pipe.- 1–12 steps. The response is receipts only:
{ steps: [{function, chars}], value_preview }(preview_charssizes the preview, default 400, capped at 8000 — the receipt must never become the bulk channel it replaces). - A failing step stops the pipe; the error carries the completed-step trail.
Boundaries
- Steps run with THIS worker's authority, not the calling agent's per-step
dispatch policy — the
fp::pipecall itself is the policy/approval surface (an approver sees the full step list). For that reason the pipe is not agent-callable without approval by default; the pure transforms are. Seeiii-permissions.yaml. shell::*/coder::*steps run under the harness-forwarded filesystem scope: the harness stamps the trustedfs_scopeonto thefp::pipecall itself (harness/src/filesystem_scope.rs) and fp re-stamps it onto each scoped step as the last write before dispatch, overwriting anything authored or threaded at/fs_scope. Without a stamp (no session working directory, or a non-harness caller — cron, worker-to-worker) scoped steps are refused, fail-closed. A path-access rejection inside a pipe just fails the step; only a direct call offers the access-grant ladder.- Statically refused as steps:
engine::register_trigger/engine::unregister_trigger(need the harness trusted-session stamp), nested pipes, and every class the agent policy hard-denies —session::*/approval::*,configuration::*/oauth::*(credentials),router::*/provider::*(model spend),harness::*/run::*(turn control),stream::*and bus internals,*::on-config-change— because steps run with worker authority and must not ride past those denies.state::*is the deliberate exception: persisting the threaded value is the pipe's purpose, and the pipe call itself is the approval surface.
api reference (json)
{
"functions": [
{
"description": "Remove null elements from an array (lodash _.compact, null-only: 0, false and \"\" are KEPT so plucked data survives): { value }. Pairs with fp::map, whose sporadic misses become null. Mainly useful as a fp::pipe step.",
"metadata": {},
"name": "fp::compact",
"request_schema": {
"$schema": "http://json-schema.org/draft-07/schema#",
"description": "Shared request for the value-only ops (uniq/size/compact/flatten/reverse).",
"properties": {
"value": {
"description": "Input value (a pipe lands the previous step's value here)."
}
},
"required": [
"value"
],
"title": "ValueRequest",
"type": "object"
},
"response_schema": {
"$schema": "http://json-schema.org/draft-07/schema#",
"properties": {
"value": true
},
"required": [
"value"
],
"title": "UtilResponse",
"type": "object"
}
},
{
"description": "Trigger-binding guard: compare a JSON pointer inside the fired event and answer the condition contract ({ decision: \"allow\" | \"skip\", reason }). Config is { path?, op, to?, negate? } with the fp::when ops (==, !=, >, >=, <, <=, exists, not_empty); a pointer that resolves to nothing SKIPS rather than erroring, so \"not there yet\" is an ordinary answer. Use it as a binding's `conditions` entry so a reaction fires only on events that matter — it is not a fan-in gate (that is state::barrier), it filters one event at a time.",
"metadata": {},
"name": "fp::condition",
"request_schema": {
"$schema": "http://json-schema.org/draft-07/schema#",
"definitions": {
"ConditionConfig": {
"description": "The caller-authored half, out of the binding's `conditions[].config`.",
"properties": {
"negate": {
"default": false,
"description": "Invert the verdict. The only way to say \"fire when this is ABSENT\", since a pointer miss fails the guard and there is no `not_exists` op. Note the footgun that comes with it: a path that never resolves then fires on EVERY event.",
"type": "boolean"
},
"op": {
"allOf": [
{
"$ref": "#/definitions/WhenOp"
}
],
"description": "One of \"==\", \"!=\", \">\", \">=\", \"<\", \"<=\", \"exists\", \"not_empty\"."
},
"path": {
"default": null,
"description": "JSON pointer into the EVENT (default: the whole event). Almost always wanted — comparing a whole event object is rarely what you mean.",
"type": [
"string",
"null"
]
},
"to": {
"default": null,
"description": "Right-hand side for the comparison ops; rejected for exists/not_empty."
}
},
"required": [
"op"
],
"type": "object"
},
"WhenOp": {
"description": "The comparison a `fp::when` guard runs.",
"enum": [
"==",
"!=",
">",
">=",
"<",
"<=",
"exists",
"not_empty"
],
"type": "string"
}
},
"description": "The condition envelope. `binding` and `context` are accepted and ignored so the same function also works as a direct call.",
"properties": {
"condition_config": {
"anyOf": [
{
"$ref": "#/definitions/ConditionConfig"
},
{
"type": "null"
}
]
},
"event": true
},
"required": [
"event"
],
"title": "ConditionInput",
"type": "object"
},
"response_schema": {
"$schema": "http://json-schema.org/draft-07/schema#",
"description": "The typed answer a binding's condition contract expects.",
"oneOf": [
{
"properties": {
"decision": {
"enum": [
"allow"
],
"type": "string"
},
"reason": {
"type": "string"
}
},
"required": [
"decision",
"reason"
],
"type": "object"
},
{
"properties": {
"decision": {
"enum": [
"skip"
],
"type": "string"
},
"reason": {
"type": "string"
}
},
"required": [
"decision",
"reason"
],
"type": "object"
}
],
"title": "Decision"
}
},
{
"description": "Count array elements per plucked key (lodash _.countBy): { value, path: \"/writer\" } -> { \"<key>\": <count> } (`\"\"` counts by the element itself). Same key rules as fp::groupBy. Pair with fp::groupBy + fp::sum for a per-key total. Mainly useful as a fp::pipe step.",
"metadata": {},
"name": "fp::countBy",
"request_schema": {
"$schema": "http://json-schema.org/draft-07/schema#",
"description": "Shared request for the keyed groupings (groupBy/countBy).",
"properties": {
"path": {
"description": "JSON pointer plucked from each element as the bucket key (`\"\"` = the element itself).",
"type": "string"
},
"value": {
"description": "Input value (a pipe lands the previous step's value here)."
}
},
"required": [
"path",
"value"
],
"title": "GroupByRequest",
"type": "object"
},
"response_schema": {
"$schema": "http://json-schema.org/draft-07/schema#",
"properties": {
"value": true
},
"required": [
"value"
],
"title": "UtilResponse",
"type": "object"
}
},
{
"description": "Skip the first n elements of an array or the first n characters of a string (lodash _.drop): { value, n }. Mainly useful as a fp::pipe step.",
"metadata": {},
"name": "fp::drop",
"request_schema": {
"$schema": "http://json-schema.org/draft-07/schema#",
"properties": {
"n": {
"description": "How many array elements / string characters to skip.",
"format": "uint32",
"minimum": 0,
"type": "integer"
},
"value": {
"description": "Input value (a pipe lands the previous step's value here)."
}
},
"required": [
"n",
"value"
],
"title": "DropRequest",
"type": "object"
},
"response_schema": {
"$schema": "http://json-schema.org/draft-07/schema#",
"properties": {
"value": true
},
"required": [
"value"
],
"title": "UtilResponse",
"type": "object"
}
},
{
"description": "Keep array elements whose properties equal a partial object (lodash _.filter with a matches iteratee): { value, matches: { status: \"active\" } }. matches is a partial OBJECT (never a bare string/number) and only object elements can match. Mainly useful as a fp::pipe step.",
"metadata": {},
"name": "fp::filter",
"request_schema": {
"$schema": "http://json-schema.org/draft-07/schema#",
"properties": {
"matches": {
"additionalProperties": true,
"description": "Partial object an element's top-level properties must equal.",
"type": "object"
},
"value": {
"description": "Input value (a pipe lands the previous step's value here)."
}
},
"required": [
"matches",
"value"
],
"title": "FilterRequest",
"type": "object"
},
"response_schema": {
"$schema": "http://json-schema.org/draft-07/schema#",
"properties": {
"value": true
},
"required": [
"value"
],
"title": "UtilResponse",
"type": "object"
}
},
{
"description": "Flatten an array one level (lodash _.flatten): [1,[2],[[3]]] -> [1,2,[3]]. Mainly useful as a fp::pipe step.",
"metadata": {},
"name": "fp::flatten",
"request_schema": {
"$schema": "http://json-schema.org/draft-07/schema#",
"description": "Shared request for the value-only ops (uniq/size/compact/flatten/reverse).",
"properties": {
"value": {
"description": "Input value (a pipe lands the previous step's value here)."
}
},
"required": [
"value"
],
"title": "ValueRequest",
"type": "object"
},
"response_schema": {
"$schema": "http://json-schema.org/draft-07/schema#",
"properties": {
"value": true
},
"required": [
"value"
],
"title": "UtilResponse",
"type": "object"
}
},
{
"description": "Extract the single value at a JSON pointer (lodash _.get): { value, path: \"/content\" }. A miss names the keys that were available. Mainly useful as a fp::pipe step.",
"metadata": {},
"name": "fp::get",
"request_schema": {
"$schema": "http://json-schema.org/draft-07/schema#",
"properties": {
"path": {
"description": "JSON pointer selecting the value to return (e.g. `/content`).",
"type": "string"
},
"value": {
"description": "Input value (a pipe lands the previous step's value here)."
}
},
"required": [
"path",
"value"
],
"title": "GetRequest",
"type": "object"
},
"response_schema": {
"$schema": "http://json-schema.org/draft-07/schema#",
"properties": {
"value": true
},
"required": [
"value"
],
"title": "UtilResponse",
"type": "object"
}
},
{
"description": "Extract the value at a JSON pointer, or `default` when the path misses (lodash fp.getOr): { value, path, default }. A stored null is a hit, not a miss. Mainly useful as a fp::pipe step.",
"metadata": {},
"name": "fp::getOr",
"request_schema": {
"$schema": "http://json-schema.org/draft-07/schema#",
"properties": {
"default": {
"description": "Returned when the path matches nothing (a stored null is a hit)."
},
"path": {
"description": "JSON pointer selecting the value to return (e.g. `/content`).",
"type": "string"
},
"value": {
"description": "Input value (a pipe lands the previous step's value here)."
}
},
"required": [
"default",
"path",
"value"
],
"title": "GetOrRequest",
"type": "object"
},
"response_schema": {
"$schema": "http://json-schema.org/draft-07/schema#",
"properties": {
"value": true
},
"required": [
"value"
],
"title": "UtilResponse",
"type": "object"
}
},
{
"description": "Bucket array elements by a plucked key (lodash _.groupBy): { value, path: \"/writer\" } -> { \"<key>\": [element, ...] } (`\"\"` groups by the element itself). Keys must be a string, number, or boolean; a null or container key errors rather than collapsing distinct groups into one bucket. Mainly useful as a fp::pipe step.",
"metadata": {},
"name": "fp::groupBy",
"request_schema": {
"$schema": "http://json-schema.org/draft-07/schema#",
"description": "Shared request for the keyed groupings (groupBy/countBy).",
"properties": {
"path": {
"description": "JSON pointer plucked from each element as the bucket key (`\"\"` = the element itself).",
"type": "string"
},
"value": {
"description": "Input value (a pipe lands the previous step's value here)."
}
},
"required": [
"path",
"value"
],
"title": "GroupByRequest",
"type": "object"
},
"response_schema": {
"$schema": "http://json-schema.org/draft-07/schema#",
"properties": {
"value": true
},
"required": [
"value"
],
"title": "UtilResponse",
"type": "object"
}
},
{
"description": "Internal pre_generate hook: appends fp::pipe usage guidance to the agent system prompt. Bound to harness::hook::pre-generate at worker startup; not called directly.",
"metadata": {
"internal": true
},
"name": "fp::inject-guidance",
"request_schema": {
"$schema": "http://json-schema.org/draft-07/schema#",
"definitions": {
"GenerateContext": {
"properties": {
"system_prompt": {
"default": "",
"description": "The system prompt assembled so far (base + any prior hook's mutation).",
"type": "string"
}
},
"type": "object"
}
},
"description": "The slice of the `pre_generate` hook envelope we read (lenient: ignores every other field the harness sends). The harness nests the live generation context under `generate` (see harness `HookRunner::run_pre_generate`).",
"properties": {
"generate": {
"$ref": "#/definitions/GenerateContext"
}
},
"title": "PreGenerateEvent",
"type": "object"
},
"response_schema": {
"$schema": "http://json-schema.org/draft-07/schema#",
"definitions": {
"PreGenerateMutations": {
"description": "The harness applies `system_prompt` only when the key is present (`HookRunner::run_pre_generate` — `if m.system_prompt.is_some()`), so `None` serializes to an empty object: the safe no-op that preserves the harness's assembled prompt.",
"properties": {
"system_prompt": {
"description": "Full replacement system prompt (base + appended guidance). The harness overwrites, it does not merge.",
"type": [
"string",
"null"
]
}
},
"type": "object"
}
},
"description": "Hook envelope returned to the harness: the mutations to apply to the generation.",
"properties": {
"mutations": {
"$ref": "#/definitions/PreGenerateMutations"
}
},
"required": [
"mutations"
],
"title": "PreGenerateResponse",
"type": "object"
}
},
{
"description": "Join array elements into one string (lodash _.join): { value, separator } (separator defaults to \",\"). Mainly useful as a fp::pipe step.",
"metadata": {},
"name": "fp::join",
"request_schema": {
"$schema": "http://json-schema.org/draft-07/schema#",
"properties": {
"separator": {
"default": ",",
"description": "Separator between elements (default `\",\"`).",
"type": "string"
},
"value": {
"description": "Input value (a pipe lands the previous step's value here)."
}
},
"required": [
"value"
],
"title": "JoinRequest",
"type": "object"
},
"response_schema": {
"$schema": "http://json-schema.org/draft-07/schema#",
"properties": {
"value": true
},
"required": [
"value"
],
"title": "UtilResponse",
"type": "object"
}
},
{
"description": "Pluck the value at a JSON pointer from each array element (lodash _.map with a property iteratee): { value, path: \"/id\" }. Sporadic misses become null (fp::compact drops them), but a path matching NO element errors — pointers pluck stored fields, not computed properties like /length (fp::size counts). Mainly useful as a fp::pipe step.",
"metadata": {},
"name": "fp::map",
"request_schema": {
"$schema": "http://json-schema.org/draft-07/schema#",
"properties": {
"path": {
"description": "JSON pointer plucked from each element (e.g. `/id`). Sporadic misses become null; a path matching NO element is an error (stored fields only — no computed properties like `/length`).",
"type": "string"
},
"value": {
"description": "Input value (a pipe lands the previous step's value here)."
}
},
"required": [
"path",
"value"
],
"title": "MapRequest",
"type": "object"
},
"response_schema": {
"$schema": "http://json-schema.org/draft-07/schema#",
"properties": {
"value": true
},
"required": [
"value"
],
"title": "UtilResponse",
"type": "object"
}
},
{
"description": "Largest number in an array (lodash _.max/_.maxBy): { value }, or { value, path: \"/amount\" }. Deviates from lodash _.maxBy: returns the NUMBER, not the element holding it, so a fp::when guard can compare it directly. An empty array errors. Mainly useful as a fp::pipe step.",
"metadata": {},
"name": "fp::max",
"request_schema": {
"$schema": "http://json-schema.org/draft-07/schema#",
"description": "Shared request for the numeric reductions (sum/mean/min/max).",
"properties": {
"path": {
"default": null,
"description": "Optional JSON pointer plucked from each element before reducing, so `[{amount: 3}, …]` reduces without a separate fp::map step.",
"type": [
"string",
"null"
]
},
"value": {
"description": "Input value (a pipe lands the previous step's value here)."
}
},
"required": [
"value"
],
"title": "ReduceRequest",
"type": "object"
},
"response_schema": {
"$schema": "http://json-schema.org/draft-07/schema#",
"properties": {
"value": true
},
"required": [
"value"
],
"title": "UtilResponse",
"type": "object"
}
},
{
"description": "Arithmetic mean of an array of numbers (lodash _.mean/_.meanBy): { value }, or { value, path: \"/score\" }. Deviates from lodash: an EMPTY array errors instead of yielding NaN, which would thread garbage into the next step. Mainly useful as a fp::pipe step.",
"metadata": {},
"name": "fp::mean",
"request_schema": {
"$schema": "http://json-schema.org/draft-07/schema#",
"description": "Shared request for the numeric reductions (sum/mean/min/max).",
"properties": {
"path": {
"default": null,
"description": "Optional JSON pointer plucked from each element before reducing, so `[{amount: 3}, …]` reduces without a separate fp::map step.",
"type": [
"string",
"null"
]
},
"value": {
"description": "Input value (a pipe lands the previous step's value here)."
}
},
"required": [
"value"
],
"title": "ReduceRequest",
"type": "object"
},
"response_schema": {
"$schema": "http://json-schema.org/draft-07/schema#",
"properties": {
"value": true
},
"required": [
"value"
],
"title": "UtilResponse",
"type": "object"
}
},
{
"description": "Smallest number in an array (lodash _.min/_.minBy): { value }, or { value, path: \"/amount\" }. Deviates from lodash _.minBy: returns the NUMBER, not the element holding it, so a fp::when guard can compare it directly. An empty array errors. Mainly useful as a fp::pipe step.",
"metadata": {},
"name": "fp::min",
"request_schema": {
"$schema": "http://json-schema.org/draft-07/schema#",
"description": "Shared request for the numeric reductions (sum/mean/min/max).",
"properties": {
"path": {
"default": null,
"description": "Optional JSON pointer plucked from each element before reducing, so `[{amount: 3}, …]` reduces without a separate fp::map step.",
"type": [
"string",
"null"
]
},
"value": {
"description": "Input value (a pipe lands the previous step's value here)."
}
},
"required": [
"value"
],
"title": "ReduceRequest",
"type": "object"
},
"response_schema": {
"$schema": "http://json-schema.org/draft-07/schema#",
"properties": {
"value": true
},
"required": [
"value"
],
"title": "UtilResponse",
"type": "object"
}
},
{
"description": "Element at index n; negative n counts from the end (lodash _.nth): { value, n: -1 } is the last element. Out of bounds errors naming the length. Mainly useful as a fp::pipe step.",
"metadata": {},
"name": "fp::nth",
"request_schema": {
"$schema": "http://json-schema.org/draft-07/schema#",
"properties": {
"n": {
"description": "Index to pluck; negative counts from the end (`-1` = last element).",
"format": "int64",
"type": "integer"
},
"value": {
"description": "Input value (a pipe lands the previous step's value here)."
}
},
"required": [
"n",
"value"
],
"title": "NthRequest",
"type": "object"
},
"response_schema": {
"$schema": "http://json-schema.org/draft-07/schema#",
"properties": {
"value": true
},
"required": [
"value"
],
"title": "UtilResponse",
"type": "object"
}
},
{
"description": "Drop top-level keys from an object (lodash _.omit): { value, paths: [\"a\",\"b\"] }. Mainly useful as a fp::pipe step.",
"metadata": {},
"name": "fp::omit",
"request_schema": {
"$schema": "http://json-schema.org/draft-07/schema#",
"properties": {
"paths": {
"description": "Top-level keys to drop; missing keys are ignored.",
"items": {
"type": "string"
},
"type": "array"
},
"value": {
"description": "Input value (a pipe lands the previous step's value here)."
}
},
"required": [
"paths",
"value"
],
"title": "OmitRequest",
"type": "object"
},
"response_schema": {
"$schema": "http://json-schema.org/draft-07/schema#",
"properties": {
"value": true
},
"required": [
"value"
],
"title": "UtilResponse",
"type": "object"
}
},
{
"description": "Subset an object by top-level keys (lodash _.pick): { value, paths: [\"a\",\"b\"] } -> {a, b}; missing keys are omitted. Mainly useful as a fp::pipe step.",
"metadata": {},
"name": "fp::pick",
"request_schema": {
"$schema": "http://json-schema.org/draft-07/schema#",
"properties": {
"paths": {
"description": "Top-level keys to keep; missing keys are silently omitted.",
"items": {
"type": "string"
},
"type": "array"
},
"value": {
"description": "Input value (a pipe lands the previous step's value here)."
}
},
"required": [
"paths",
"value"
],
"title": "PickRequest",
"type": "object"
},
"response_schema": {
"$schema": "http://json-schema.org/draft-07/schema#",
"properties": {
"value": true
},
"required": [
"value"
],
"title": "UtilResponse",
"type": "object"
}
},
{
"description": "Run a short worker-side pipeline in one call: each step triggers a function and its result lands in the next step's payload at `into` (default \"/value\") — large values flow worker→worker and never enter the chat. fp::* transform steps (get/pick/omit/take/drop/map/filter/split/join/uniq/size/compact/nth/getOr/flatten/sortBy/reverse/when, lodash semantics) run inline and thread the transformed value itself — the {value} wrapper they return when called directly never appears between steps. A failing fp::when guard STOPS the pipe (`short_circuited: true`), so a trailing write step runs only when its condition holds. The FIRST step receives no threaded value: start with a producing function (a fetch, a state::get) or seed a leading transform via payload.value. Example: fetch an article and persist it trimmed: through=[{function:'scrapling::fetch', payload:{url,format:'markdown'}}, {function:'fp::get', payload:{path:'/content'}}, {function:'fp::take', payload:{n:20000}}, {function:'state::set', payload:{scope,key}}]. Returns per-step sizes and a preview, not the value. shell::*/coder::* steps run under the session's harness-stamped filesystem scope; without one (no working directory, or a non-harness caller) they are refused — call them directly instead. Database write steps are refused only while `approval::gate` is registered.",
"metadata": {},
"name": "fp::pipe",
"request_schema": {
"$schema": "http://json-schema.org/draft-07/schema#",
"definitions": {
"PipeStep": {
"additionalProperties": false,
"properties": {
"function": {
"description": "Function id to trigger (e.g. `scrapling::fetch`, `fp::get`, `state::set`).",
"type": "string"
},
"into": {
"default": null,
"description": "JSON pointer in THIS step's payload where the previous step's value lands (default `/value`). Object keys only (`~0`/`~1` escapes are decoded); indexing into arrays is unsupported.",
"type": [
"string",
"null"
]
},
"payload": {
"default": null,
"description": "Base payload for the call."
}
},
"required": [
"function"
],
"type": "object"
}
},
"properties": {
"preview_chars": {
"default": null,
"description": "Preview length of the final threaded value in the receipt (capped at 8000).",
"format": "uint32",
"minimum": 0,
"type": [
"integer",
"null"
]
},
"through": {
"description": "Steps run in order; step N+1 receives step N's threaded value.",
"items": {
"$ref": "#/definitions/PipeStep"
},
"type": "array"
}
},
"required": [
"through"
],
"title": "PipeRequest",
"type": "object"
},
"response_schema": {
"$schema": "http://json-schema.org/draft-07/schema#",
"definitions": {
"StepReceipt": {
"properties": {
"chars": {
"description": "Size of the value threaded OUT of this step (chars of its string or serialized form).",
"format": "uint",
"minimum": 0,
"type": "integer"
},
"function": {
"type": "string"
},
"note": {
"description": "Set when the threaded value REPLACED a non-null literal already in this step's payload at the `into` pointer — almost always an omitted `into` on a write step clobbering the value it meant to write.",
"type": [
"string",
"null"
]
}
},
"required": [
"chars",
"function"
],
"type": "object"
}
},
"properties": {
"short_circuited": {
"description": "True when a `fp::when` guard failed and stopped the pipe: the receipts end at the guard and NO later step ran. Absent (false) on a full run.",
"type": "boolean"
},
"steps": {
"items": {
"$ref": "#/definitions/StepReceipt"
},
"type": "array"
},
"value_preview": {
"type": [
"string",
"null"
]
}
},
"required": [
"short_circuited",
"steps"
],
"title": "PipeResponse",
"type": "object"
}
},
{
"description": "Reverse an array (lodash _.reverse, immutable like lodash/fp): { value }. Mainly useful as a fp::pipe step.",
"metadata": {},
"name": "fp::reverse",
"request_schema": {
"$schema": "http://json-schema.org/draft-07/schema#",
"description": "Shared request for the value-only ops (uniq/size/compact/flatten/reverse).",
"properties": {
"value": {
"description": "Input value (a pipe lands the previous step's value here)."
}
},
"required": [
"value"
],
"title": "ValueRequest",
"type": "object"
},
"response_schema": {
"$schema": "http://json-schema.org/draft-07/schema#",
"properties": {
"value": true
},
"required": [
"value"
],
"title": "UtilResponse",
"type": "object"
}
},
{
"description": "Count a collection (lodash _.size): array length, string chars, or object key count: { value }. Non-collections error instead of returning 0. Mainly useful as a fp::pipe step.",
"metadata": {},
"name": "fp::size",
"request_schema": {
"$schema": "http://json-schema.org/draft-07/schema#",
"description": "Shared request for the value-only ops (uniq/size/compact/flatten/reverse).",
"properties": {
"value": {
"description": "Input value (a pipe lands the previous step's value here)."
}
},
"required": [
"value"
],
"title": "ValueRequest",
"type": "object"
},
"response_schema": {
"$schema": "http://json-schema.org/draft-07/schema#",
"properties": {
"value": true
},
"required": [
"value"
],
"title": "UtilResponse",
"type": "object"
}
},
{
"description": "Sort array elements ascending by the value at a JSON pointer (lodash _.sortBy, stable): { value, path } — path \"\" sorts the elements themselves (primitives). Keys must all be numbers, strings, or booleans; an element missing the path errors. Pair with fp::reverse for descending. Mainly useful as a fp::pipe step.",
"metadata": {},
"name": "fp::sortBy",
"request_schema": {
"$schema": "http://json-schema.org/draft-07/schema#",
"properties": {
"path": {
"description": "JSON pointer plucked from each element as the sort key (e.g. `/score`).",
"type": "string"
},
"value": {
"description": "Input value (a pipe lands the previous step's value here)."
}
},
"required": [
"path",
"value"
],
"title": "SortByRequest",
"type": "object"
},
"response_schema": {
"$schema": "http://json-schema.org/draft-07/schema#",
"properties": {
"value": true
},
"required": [
"value"
],
"title": "UtilResponse",
"type": "object"
}
},
{
"description": "Split a string into an array of strings (lodash _.split): { value, separator: \"\\n\" }. Mainly useful as a fp::pipe step.",
"metadata": {},
"name": "fp::split",
"request_schema": {
"$schema": "http://json-schema.org/draft-07/schema#",
"properties": {
"separator": {
"description": "Non-empty separator to split on (e.g. `\"\\n\"`).",
"type": "string"
},
"value": {
"description": "Input value (a pipe lands the previous step's value here)."
}
},
"required": [
"separator",
"value"
],
"title": "SplitRequest",
"type": "object"
},
"response_schema": {
"$schema": "http://json-schema.org/draft-07/schema#",
"properties": {
"value": true
},
"required": [
"value"
],
"title": "UtilResponse",
"type": "object"
}
},
{
"description": "Total an array of numbers (lodash _.sum/_.sumBy): { value }, or { value, path: \"/amount\" } to pluck the addend from each element first. An empty array totals 0; a non-numeric element errors rather than being skipped. All-integer inputs total to an integer, so the result compares cleanly in a fp::when guard. Mainly useful as a fp::pipe step.",
"metadata": {},
"name": "fp::sum",
"request_schema": {
"$schema": "http://json-schema.org/draft-07/schema#",
"description": "Shared request for the numeric reductions (sum/mean/min/max).",
"properties": {
"path": {
"default": null,
"description": "Optional JSON pointer plucked from each element before reducing, so `[{amount: 3}, …]` reduces without a separate fp::map step.",
"type": [
"string",
"null"
]
},
"value": {
"description": "Input value (a pipe lands the previous step's value here)."
}
},
"required": [
"value"
],
"title": "ReduceRequest",
"type": "object"
},
"response_schema": {
"$schema": "http://json-schema.org/draft-07/schema#",
"properties": {
"value": true
},
"required": [
"value"
],
"title": "UtilResponse",
"type": "object"
}
},
{
"description": "Keep the first n elements of an array or the first n characters of a string (lodash _.take): { value, n }. Mainly useful as a fp::pipe step.",
"metadata": {},
"name": "fp::take",
"request_schema": {
"$schema": "http://json-schema.org/draft-07/schema#",
"properties": {
"n": {
"description": "How many array elements / string characters to keep.",
"format": "uint32",
"minimum": 0,
"type": "integer"
},
"value": {
"description": "Input value (a pipe lands the previous step's value here)."
}
},
"required": [
"n",
"value"
],
"title": "TakeRequest",
"type": "object"
},
"response_schema": {
"$schema": "http://json-schema.org/draft-07/schema#",
"properties": {
"value": true
},
"required": [
"value"
],
"title": "UtilResponse",
"type": "object"
}
},
{
"description": "Deduplicate an array, keeping first occurrences (lodash _.uniq): { value }. Mainly useful as a fp::pipe step.",
"metadata": {},
"name": "fp::uniq",
"request_schema": {
"$schema": "http://json-schema.org/draft-07/schema#",
"description": "Shared request for the value-only ops (uniq/size/compact/flatten/reverse).",
"properties": {
"value": {
"description": "Input value (a pipe lands the previous step's value here)."
}
},
"required": [
"value"
],
"title": "ValueRequest",
"type": "object"
},
"response_schema": {
"$schema": "http://json-schema.org/draft-07/schema#",
"properties": {
"value": true
},
"required": [
"value"
],
"title": "UtilResponse",
"type": "object"
}
},
{
"description": "Guard: test the value at a JSON pointer ({ value, path?, op, to? }; ops ==, !=, >, >=, <, <=, exists, not_empty; path defaults to the whole value; a pointer miss FAILS the guard, it never errors). Direct calls return { passed, value }. As a fp::pipe step, a passing guard threads the ORIGINAL value onward unchanged and a failing one STOPS the pipe (`short_circuited: true`), so a trailing write step (e.g. state::set) runs only when the condition holds.",
"metadata": {},
"name": "fp::when",
"request_schema": {
"$schema": "http://json-schema.org/draft-07/schema#",
"definitions": {
"WhenOp": {
"description": "The comparison a `fp::when` guard runs.",
"enum": [
"==",
"!=",
">",
">=",
"<",
"<=",
"exists",
"not_empty"
],
"type": "string"
}
},
"properties": {
"op": {
"allOf": [
{
"$ref": "#/definitions/WhenOp"
}
],
"description": "One of \"==\", \"!=\", \">\", \">=\", \"<\", \"<=\", \"exists\", \"not_empty\"."
},
"path": {
"default": null,
"description": "JSON pointer selecting what to test (default: the whole value).",
"type": [
"string",
"null"
]
},
"to": {
"default": null,
"description": "Right-hand side for the comparison ops; meaningless (and rejected) for exists / not_empty."
},
"value": {
"description": "Input value (a pipe lands the previous step's value here)."
}
},
"required": [
"op",
"value"
],
"title": "WhenRequest",
"type": "object"
},
"response_schema": {
"$schema": "http://json-schema.org/draft-07/schema#",
"properties": {
"passed": {
"type": "boolean"
},
"value": {
"description": "The ORIGINAL input value, untouched — guards test, they never reshape."
}
},
"required": [
"passed",
"value"
],
"title": "WhenResponse",
"type": "object"
}
}
],
"triggers": []
}