fcode-factorial-actions
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseFactorial Code — Factorial Actions
Factorial Code — Factorial Actions
A Factorial Action is a process that the customers who installed an App
reach from inside Factorial, without knowing Factorial Code exists. One
process is one action, and the same action can be exposed through several
entry points at once — a button in the Factorial UI, a form, a Factorial One
tool, a backend job. Whatever the entry point, Factorial sends the process its
parameters and, when asked to, waits for the result; the process does not know
which entry point started it, so one implementation serves all of them.
Everything an action needs lives in one settings block, : the
Trigger from Factorial section of the process page in the console, or the
key of (round-tripped by
/ , field reference in ).
factorialfactorialprocesses/<slug>/metadata.jsonfcode pullfcode pushfcode-cliFactorial Action 是安装 App 的客户可在 Factorial 内部使用的一种流程,而客户甚至无需知道 Factorial Code 的存在。一个流程对应一个 Action,同一个 Action 也可以同时通过多个 入口点 对外暴露,例如 Factorial UI 中的按钮、表单、Factorial One 工具或后端任务。无论从哪个入口点进入,Factorial 都会向流程发送参数,并按要求等待结果;流程并不知道是哪个入口点启动了它,因此同一套实现可服务所有入口点。
Action 所需的所有配置都位于一个设置块 中:控制台流程页面的 从 Factorial 触发 区域,或 的 键( / 可对其进行往返同步,字段参考见 )。
factorialprocesses/<slug>/metadata.jsonfactorialfcode pullfcode pushfcode-cliWhat is live today and what is not
当前支持的功能与尚未支持的功能
| Available now | Still to come |
|---|---|
The | Factorial One tools: the assistant reading the |
| The Actions API Factorial invokes through: list a company's actions, read an action's form schema, invoke it, issue upload grants, and list an app's declared actions before it is installed | Enforcing |
| |
| |
| |
File parameters ( | |
| |
|
So today , , and all do
something. and are stored and reachable, but no assistant
reads a tool contract yet — fill them in now so the action is ready when it
does.
uiTriggerformrequiredPoliciesawaitResultagentToolbackend| 当前可用 | 即将推出 |
|---|---|
控制台、 | Factorial One 工具:助手读取 |
| Factorial 调用的 Actions API:列出公司的 Action、读取 Action 的表单 schema、调用 Action、签发上传授权,以及在 App 安装前列出该 App 声明的 Action | 强制执行 |
每次调用时,在任何内容运行之前强制执行 | |
| |
| |
文件参数( | |
| |
|
因此,目前 、、 和 都会实际生效。 和 会被存储且可以访问,但目前还没有助手读取工具契约——现在先填写好,等助手支持时即可直接使用。
uiTriggerformrequiredPoliciesawaitResultagentToolbackendGotchas
常见陷阱
- is a master switch. While it is
enablednothing below is exposed, whatever each entry point's ownfalsesays. Turning it back on restores the entry points exactly as they were.enabled - defaults to
awaitResult(synchronous). The legacytruedefaulted to fire-and-forget. The mirror copiesuiTriggerintofactorial.awaitResult, so a button moved to theuiTrigger.awaitResultblock becomes synchronous unless you writefactorial. Keep synchronous actions under the ~50 s budget ("awaitResult": false).fcode-ui-triggers - are enforced, but the keys are free text. Factorial checks them before it runs anything. It does not check that a key exists: one it does not recognise is simply never granted, so the whole AND-group containing it fails — a typo can only close an action, never open one. Take keys from Factorial's policy catalogue; prefer several small alternatives over one long group.
requiredPolicies - No policies means open. An empty list, or no list at all, lets anyone who
can reach the entry point run the action — the entry point's own gate (a
button's location permissions, an installed app) is all that remains. An empty
group inside a non-empty list is dropped rather than treated as "allow all",
so still requires
[["a"], []].a - Only is translatable. It is the one text users see, so it takes
uiTrigger.labeltokens (fcode.i18n("key")). Thefcode-i18ntexts are for the model — plain text, one language, never shown to users. The process's ownagentTool/nameserve humans and the marketplace card; the block has no title of its own.description - An unset is treated as
effect. Every tool fails closed (approval + "cannot be undone" warning) until you sayDESTRUCTIVEorREAD.WRITE - The four lifecycle slugs are reserved. ,
install,settings,uninstallmean something to Factorial regardless of the block. Do not give an ordinary action one of them.sync - Omit the defaults. leaves out
fcode pullwhenawaitResult,truewhen empty,requiredPoliciesandform.publicwhenform.appTool, unsetfalsetexts and empty lists, and any sub-block whoseagentToolisenabled. A process not exposed to Factorial has nofalsekey at all. A partial update leaves the unnamed fields unchanged.factorial - Legacy /
formkeys still round-trip. When a file carries both,uiTriggersendsfcode pushand lets the mirror update the rest. Prefer writingfactorial(below).factorial
- 是总开关。 当它为
enabled时,无论下方各入口点自己的false设置为何,都不会暴露任何内容。重新启用后,这些入口点会按原配置完全恢复。enabled - 默认为
awaitResult(同步)。 旧版true默认采用触发后不等待结果的方式。镜像机制会把uiTrigger复制到factorial.awaitResult,因此迁移到uiTrigger.awaitResult块的按钮除非显式写入factorial,否则都会变为同步。同步 Action 的执行时间应控制在约 50 秒的预算内("awaitResult": false)。fcode-ui-triggers - 会强制执行,但键名可以自由填写。 Factorial 会在运行任何内容之前检查它们。它不会检查某个键是否存在:无法识别的键永远不会被授予,因此包含该键的整个 AND 组都会失败——一个拼写错误只会导致 Action 被拒绝,绝不会导致其被放行。键名应取自 Factorial 的策略目录;相较于一个冗长的组,更应优先使用多个较小的备选组。
requiredPolicies - 没有策略意味着开放。 空列表或完全没有列表时,任何能访问该入口点的人都可以运行 Action——入口点自身的限制(例如按钮所在位置的权限、App 的安装状态)是唯一剩余门槛。非空列表中的空组会被丢弃,而不会被视为“允许所有”,所以 仍然要求
[["a"], []]。a - 只有 可以翻译。 它是用户唯一会看到的文本,因此可使用
uiTrigger.labeltoken(fcode.i18n("key"))。fcode-i18n中的文本面向模型——使用纯文本、只采用一种语言,绝不会展示给用户。流程自身的agentTool/name供用户阅读并用于 marketplace 卡片;此块本身没有标题。description - 未设置 时会按
effect处理。 在明确指定DESTRUCTIVE或READ之前,所有工具都会要求审批并显示“无法撤销”警告,也就是默认采取保守拒绝策略。WRITE - 四个生命周期 slug 是保留的。 无论配置块内容如何,、
install、settings、uninstall对 Factorial 都有特定含义。不要为普通 Action 使用其中任何一个。sync - 省略默认值。 当 为
awaitResult、true为空、requiredPolicies和form.public为form.appTool、false文本未设置或列表为空,以及任意子块的agentTool为enabled时,false都不会输出这些字段。未向 Factorial 暴露的流程完全没有fcode pull键。局部更新会保持未指定字段不变。factorial - 旧版 /
form键仍可往返读写。 当一个文件同时包含两者时,uiTrigger会发送fcode push,并让镜像机制更新其余部分。请优先编写factorial(见下文)。factorial
The factorial
block, field by field
factorialfactorial
块:逐字段说明
factorialjson
{
"factorial": {
"enabled": true,
"awaitResult": false,
"requiredPolicies": [["company.manage_timeoff"], ["company.admin"]],
"uiTrigger": { "enabled": true, "locationId": "calendar.header.admin", "label": "fcode.i18n(\"acme.approve.button\")", "icon": "Bell" },
"form": { "enabled": true, "public": false, "appTool": true },
"agentTool": { "enabled": true, "description": "…", "effect": "WRITE" },
"backend": { "enabled": true }
}
}| Field | Type / default | Meaning |
|---|---|---|
| boolean, | Master switch for every entry point below |
| boolean, | |
| | OR of AND-groups of Factorial policy keys the user must hold: |
| boolean | Render the action as a button inside Factorial |
| string | Factorial UI location the button renders at (e.g. |
| string | Button text. The only i18n field: plain text or |
| string | Allowlisted icon name ( |
| boolean | Expose the parameters as a form ( |
| boolean, | Offer the form to the company's users on the App's own page in Factorial, among the things the App lets them run — a "Report a sync issue" or "Sync now" form. Leave it |
| boolean, | Deprecated. Declares the form may also be embedded anonymously outside Factorial, as the legacy embed does. Stored, not enforced yet; set it only on forms that genuinely need anonymous access |
| boolean | Let the Factorial One assistant call the process as a tool. The rest of the sub-block is the tool contract (next section) |
| boolean | Let Factorial backend jobs run the process with no user in front of it |
json
{
"factorial": {
"enabled": true,
"awaitResult": false,
"requiredPolicies": [["company.manage_timeoff"], ["company.admin"]],
"uiTrigger": { "enabled": true, "locationId": "calendar.header.admin", "label": "fcode.i18n(\"acme.approve.button\")", "icon": "Bell" },
"form": { "enabled": true, "public": false, "appTool": true },
"agentTool": { "enabled": true, "description": "…", "effect": "WRITE" },
"backend": { "enabled": true }
}
}| 字段 | 类型 / 默认值 | 含义 |
|---|---|---|
| boolean, | 下方所有入口点的总开关 |
| boolean, | |
| | 用户必须持有的 Factorial 策略键构成 AND 组的 OR 组合: |
| boolean | 在 Factorial 内将 Action 渲染为按钮 |
| string | 按钮渲染所在的 Factorial UI 位置(例如 |
| string | 按钮文本。唯一支持 i18n 的字段:纯文本或 |
| string | 允许列表中的图标名称( |
| boolean | 将参数公开为表单( |
| boolean, | 在 Factorial 中 App 自己的页面上向公司的用户提供该表单,并将其列在 App 允许用户运行的内容中,例如“报告同步问题”或“立即同步”表单。对于由 App 自行打开的表单,应保持 |
| boolean, | 已弃用。 声明该表单还可以像旧版嵌入方式那样,在 Factorial 外部以匿名方式嵌入。该值会被存储,但目前尚未强制执行;仅当表单确实需要匿名访问时才设置 |
| boolean | 允许 Factorial One 助手将流程作为工具调用。该子块的其余字段构成工具契约(见下一节) |
| boolean | 允许 Factorial 后端任务在没有前台用户的情况下运行流程 |
The Factorial One tool contract (agentTool
)
agentToolFactorial One 工具契约(agentTool
)
agentToolThe parameters schema tells the assistant what the tool takes; this block
tells it what the tool is for. Plain text, no i18n.
| Field | Type | What it buys the model |
|---|---|---|
| string | What the tool does, one or two sentences: name the object it acts on and the outcome. This is what user requests are matched against |
| | |
| string | The situation the tool is meant for, phrased as a user would ask |
| string[] | Look-alike situations that are not a match, one per entry — name the neighbour tool to use instead |
| string[] | What must already be true before calling, one per entry; the assistant checks or asks instead of calling and failing |
| string[] | Side effects a user might assume and the tool does not have, one per entry |
| string | What happens on partial success: what is done, what is skipped, how the result says so |
| string | Slug of the process this one previews (a dry run of it). Free text, not validated yet |
参数 schema 告诉助手工具接收什么参数;此块则告诉助手工具的用途。使用纯文本,不使用 i18n。
| 字段 | 类型 | 为模型提供的信息 |
|---|---|---|
| string | 工具的作用,用一两句话说明:指出其操作的对象及产生的结果。用户请求会据此进行匹配 |
| | |
| string | 该工具适用的场景,表述方式应贴近用户提出请求时的说法 |
| string[] | 看似相似但不适用的场景,每项一个——并指出应改用的相邻工具 |
| string[] | 调用前必须已经成立的条件,每项一个;助手应检查或询问,而不是调用后失败 |
| string[] | 用户可能认为会发生、但工具实际不会产生的副作用,每项一个 |
| string | 部分成功时会发生什么:哪些已完成、哪些被跳过,以及结果如何体现 |
| string | 此工具所预览的流程 slug(即对其执行 dry run)。目前是自由文本,尚未校验 |
Writing good contract texts
编写高质量契约文本
- Plain language, no i18n, one language. Write for a model reading English,
not for a screen. Never put tokens here.
fcode.i18n(...) - : object + outcome. "Approves every pending time-off request of a team" beats "Time-off tool".
description - One situation in . If you need "or", you probably have two tools. Phrase it the way the user would ask.
whenToUse - Name neighbour tools in . "The user wants a single request approved: use
whenNotToUse." Each entry is one situation.approve-timeoff-request - are checkable facts ("the team has at least one pending request"), not advice.
preconditions - lists what a user would assume. Notifications, side writes, undo — say what is not happening so the assistant can offer the missing step.
doesNotDo - describes the partial outcome, so the assistant can explain it instead of reporting a failure.
degradation - A dry-run process points at its real one with and is itself
simulatesFor; the real one isREADorWRITE.DESTRUCTIVE
- 使用平实语言,不使用 i18n,只采用一种语言。 应面向阅读英文的模型来写,而不是面向界面。绝不要在这里使用 token。
fcode.i18n(...) - :对象 + 结果。 “批准团队中所有待处理的请假申请”优于“请假工具”。
description - 只描述一种场景。 如果需要“或”,大概说明应该拆成两个工具。表述方式应贴近用户提出请求时的说法。
whenToUse - 在 中指明相邻工具。 “用户只想批准单项申请:请使用
whenNotToUse。”每项对应一种场景。approve-timeoff-request - 是可核查的事实(“团队至少有一个待处理申请”),而不是建议。
preconditions - 列出用户可能认为会发生的操作。 通知、附带写入、撤销等——应明确说明这些操作不会发生,以便助手提供缺失步骤。
doesNotDo - 描述部分执行结果,让助手可以解释该结果,而不是将其报告为失败。
degradation - Dry run 流程通过 指向其正式流程,且自身应为
simulatesFor;正式流程则为READ或WRITE。DESTRUCTIVE
Reserved lifecycle slugs
保留的生命周期 slug
| Slug | Role | What the platform does |
|---|---|---|
| The form Factorial shows when a company installs the App | Derives |
| The form an installed company uses to configure the App | |
| Runs when the company removes the App | |
| The sync process of an integrations-framework App | |
lifecycleRolemetadata.jsoninstallsettingsuninstallfactorial.form.enabledfcode-forms| Slug | 角色 | 平台执行的操作 |
|---|---|---|
| 公司安装 App 时 Factorial 显示的表单 | 派生 |
| 已安装该 App 的公司用来配置 App 的表单 | |
| 公司移除 App 时运行 | |
| integrations-framework App 的同步流程 | |
lifecycleRolemetadata.jsoninstallsettingsuninstallfactorial.form.enabledfcode-formsParameters: the contract with Factorial
参数:与 Factorial 之间的契约
Whatever the entry point, what Factorial sends is described by the process's
(). The form renders it, the
assistant reads it to know what the tool takes, Factorial validates against it.
Give every parameter a and a , list the mandatory ones in
, and treat any schema change as a change to what Factorial may
call you with. Trusted keys a UI trigger injects (,
, ) are described in .
parametersSchema.jsonfcode-json-schematitledescriptionrequiredcompany_idtriggered_from_locationaccess_idfcode-ui-triggers无论使用哪个入口点,Factorial 发送的内容都由流程的 ()描述。表单根据它渲染,助手读取它以了解工具接受哪些参数,Factorial 则依据它进行校验。应为每个参数提供 和 ,在 中列出必填项,并将任何 schema 变更都视为 Factorial 调用流程时可以传入的参数范围发生变更。UI trigger 注入的可信键(、、)在 中有说明。
parametersSchema.jsonfcode-json-schematitledescriptionrequiredcompany_idtriggered_from_locationaccess_idfcode-ui-triggersThe transition from form
and uiTrigger
formuiTrigger从 form
和 uiTrigger
迁移
formuiTriggerThe block supersedes the Forms flag () and the UI
trigger button (). Both stay for now and the platform keeps them
mirrored: whichever side you write, the other is updated, last write wins.
factorialformuiTriggerWriting | …updates the legacy setting |
|---|---|
| |
| the same keys of |
| |
| |
| |
Writing the legacy blocks mirrors the same fields back, and
turns on as soon as any entry point is enabled.
factorial.enabledform.appRoleINSTALLSETTINGSUNINSTALLUSER_FACING_FORMform.appToolappToolappRolePrefer : write it in new processes, and when touching an old
, move the settings into it rather than editing .
factorialmetadata.jsonuiTriggerfactorialformuiTrigger写入 | 会更新旧版设置 |
|---|---|
| |
| |
| |
| |
| |
写入旧版块时,同样的字段会镜像回 ;而只要任一入口点已启用, 就会开启。
factorialfactorial.enabledform.appRoleINSTALLSETTINGSUNINSTALLUSER_FACING_FORMform.appToolform.appToolappToolappRole优先使用 :新流程应直接使用它;修改旧 时,也应将设置迁移到该块,而不是编辑 。
factorialmetadata.jsonuiTriggerWorked example — SILTRA FIE import (dry run + apply)
实战示例——SILTRA FIE 导入(dry run + apply)
Adapted from the : reads the file
a company downloads from SILTRA and reports which sick leaves it would
create, link or close in Factorial, writing nothing; does the
writing, in the background.
siltra-integration-appfie-import.msjfie-import-applyprocesses/fie-import/metadata.jsonjson
{
"name": "FIE Import",
"description": "Reads the .msj file the company downloads from SILTRA and shows what importing it would change in Factorial. Writes nothing — the import only happens after confirmation.",
"tags": ["inbound", "siltra", "fie"],
"factorial": {
"enabled": true,
"requiredPolicies": [["company.manage_timeoff"], ["company.admin"]],
"uiTrigger": {
"enabled": true,
"locationId": "calendar.header.admin",
"label": "fcode.i18n(\"calendar.import.fie.button\")",
"icon": "Upload"
},
"form": { "enabled": true },
"agentTool": {
"enabled": true,
"description": "Reads a SILTRA FIE transmission (.msj) and reports which sick leaves it would create, link or close in Factorial, and which events it would leave out and why. It writes nothing.",
"effect": "READ",
"whenToUse": "The user has downloaded a FIE file from SILTRA and wants to know what importing it would change before anything is written.",
"whenNotToUse": [
"The user has already reviewed the report and wants the leaves written: use fie-import-apply.",
"The file is not a SILTRA .msj transmission."
],
"preconditions": ["The .msj file has been uploaded and its storage reference is passed as msj_file."],
"doesNotDo": ["It does not create, change or close any leave.", "It does not notify employees or managers."],
"degradation": "If Factorial cannot be consulted, the report still lists every event in the file, marked as not compared against Factorial.",
"simulatesFor": "fie-import-apply"
}
}
}processes/fie-import-apply/metadata.jsonjson
{
"name": "FIE Import — apply",
"tags": ["inbound", "siltra", "fie"],
"factorial": {
"enabled": true,
"awaitResult": false,
"requiredPolicies": [["company.manage_timeoff"], ["company.admin"]],
"agentTool": {
"enabled": true,
"description": "Writes the sick leaves a SILTRA FIE transmission describes into Factorial: creates, links or closes them.",
"effect": "WRITE",
"whenToUse": "The user has seen the fie-import report for a file and asks to apply it.",
"whenNotToUse": ["The user only wants to know what the file would change: use fie-import."],
"preconditions": ["fie-import has reported on this same file and the user confirmed the result."],
"doesNotDo": ["It does not send SILTRA anything back.", "It does not notify employees."],
"degradation": "Leaves that fail to write are skipped and listed in the result; the rest are applied."
},
"backend": { "enabled": true }
}
}The policy keys are illustrative — take the real ones from Factorial's policy
catalogue. Read the parameters as usual (,
an reference — ).
fcode.context.parameters.msj_filefcode.storage://…fcode-forms改编自 : 读取公司从 SILTRA 下载的 文件,并报告它将在 Factorial 中创建、关联或关闭哪些病假记录,且不会写入任何数据; 则负责在后台执行写入。
siltra-integration-appfie-import.msjfie-import-applyprocesses/fie-import/metadata.jsonjson
{
"name": "FIE Import",
"description": "Reads the .msj file the company downloads from SILTRA and shows what importing it would change in Factorial. Writes nothing — the import only happens after confirmation.",
"tags": ["inbound", "siltra", "fie"],
"factorial": {
"enabled": true,
"requiredPolicies": [["company.manage_timeoff"], ["company.admin"]],
"uiTrigger": {
"enabled": true,
"locationId": "calendar.header.admin",
"label": "fcode.i18n(\"calendar.import.fie.button\")",
"icon": "Upload"
},
"form": { "enabled": true },
"agentTool": {
"enabled": true,
"description": "Reads a SILTRA FIE transmission (.msj) and reports which sick leaves it would create, link or close in Factorial, and which events it would leave out and why. It writes nothing.",
"effect": "READ",
"whenToUse": "The user has downloaded a FIE file from SILTRA and wants to know what importing it would change before anything is written.",
"whenNotToUse": [
"The user has already reviewed the report and wants the leaves written: use fie-import-apply.",
"The file is not a SILTRA .msj transmission."
],
"preconditions": ["The .msj file has been uploaded and its storage reference is passed as msj_file."],
"doesNotDo": ["It does not create, change or close any leave.", "It does not notify employees or managers."],
"degradation": "If Factorial cannot be consulted, the report still lists every event in the file, marked as not compared against Factorial.",
"simulatesFor": "fie-import-apply"
}
}
}processes/fie-import-apply/metadata.jsonjson
{
"name": "FIE Import — apply",
"tags": ["inbound", "siltra", "fie"],
"factorial": {
"enabled": true,
"awaitResult": false,
"requiredPolicies": [["company.manage_timeoff"], ["company.admin"]],
"agentTool": {
"enabled": true,
"description": "Writes the sick leaves a SILTRA FIE transmission describes into Factorial: creates, links or closes them.",
"effect": "WRITE",
"whenToUse": "The user has seen the fie-import report for a file and asks to apply it.",
"whenNotToUse": ["The user only wants to know what the file would change: use fie-import."],
"preconditions": ["fie-import has reported on this same file and the user confirmed the result."],
"doesNotDo": ["It does not send SILTRA anything back.", "It does not notify employees."],
"degradation": "Leaves that fail to write are skipped and listed in the result; the rest are applied."
},
"backend": { "enabled": true }
}
}策略键仅作示例——应从 Factorial 的策略目录获取真实键值。照常读取参数(,即 引用——)。
fcode.context.parameters.msj_filefcode.storage://…fcode-formsChecklist before shipping
发布前检查清单
- on, and only the entry points you mean to expose enabled.
factorial.enabled - decided explicitly:
awaitResultfor anything slow; a synchronous action returns a shape the entry point it runs under can read.false - keys copied from Factorial's catalogue, not guessed — or deliberately empty, which leaves the action open to anyone who reaches it.
requiredPolicies - :
agentToolset,effectnames the neighbour tools, dry runs point at their real process withwhenNotToUse.simulatesFor - No ordinary action uses ,
install,settingsoruninstallas slug; the schema has titles, descriptions andsync.required
- 已开启,并且只启用确实要公开的入口点。
factorial.enabled - 明确决定 :耗时操作使用
awaitResult;同步 Action 返回的结构必须能被其运行入口点读取。false - 键值从 Factorial 的策略目录复制而来,而不是自行猜测——或者有意留空,使任何能访问该入口点的人都可以运行 Action。
requiredPolicies - :设置
agentTool;effect指明相邻工具;dry run 通过whenNotToUse指向其正式流程。simulatesFor - 普通 Action 不使用 、
install、settings或uninstall作为 slug;schema 中配有sync、title和description。required