
CX
29/9/2026
Tóm tắt
blog.share_blog
@cogover/sdk: Custom Module ActionTrang này thuộc API reference của @cogover/sdk.
Custom Module Action là một thao tác ngắn, chạy đồng bộ của project mà Process hoặc
AI Agent có thể gọi. Process Builder đưa mỗi action mở cho "process" thành một node
Custom Module Action, và AI Agent Builder đưa mỗi action mở cho "agent" thành
một tool. Khai báo từng action bằng defineAction và liệt kê kết quả trong named
export actions của project. Default export vẫn là tuỳ chọn với project chỉ khai báo
action, trigger và job.
Ví dụ này giả định workspace.d.ts đã khai báo Object lead và các field dùng bên
dưới.
import { defineAction, s } from "@cogover/sdk";
export const actions = [
defineAction({
key: "score_lead",
label: "Score lead",
description: "Scores a lead from its recent activities and returns a tier.",
exposeTo: ["process", "agent"],
effect: "read",
input: s.object({
leadId: s.recordId("lead").describe("ID of the lead to score"),
strict: s.boolean().optional(),
}),
output: s.object({
found: s.boolean(),
score: s.number(),
tier: s.enum(["A", "B", "C"]),
}),
timeoutMs: 5000,
async handler({ data, invocation, log }, input) {
const lead = await data.object("lead").records.get(input.leadId, { fields: ["rating"] });
if (!lead) return { found: false, score: 0, tier: "C" };
const score = Number(lead.fields.rating ?? 0) * (input.strict ? 8 : 10);
log.info("Lead scored", { leadId: lead.id, source: invocation.source.type });
return { found: true, score, tier: score >= 80 ? "A" : score >= 50 ? "B" : "C" };
},
}),
];
Cogover đọc khai báo action khi một version của project được publish và cung cấp
các action của version đang active: node Process và tool của AI Agent tham chiếu
action bằng slug của project và key của action, và luôn chạy version đang active
tại thời điểm gọi. Version có khai báo không hợp lệ sẽ publish thất bại với
ACTION_MANIFEST_INVALID. Deactivate project sẽ gỡ các action của nó khỏi cả hai
builder. Một project khai báo tối đa 50 action.
Label, mô tả và schema của action hiển thị với mọi thành viên đang hoạt động của Workspace: không đặt secret trong đó.
defineAction(config): ActionDefinitionfunction defineAction<
TInput extends ObjectSchema<ObjectShape, false>,
TOutput extends ObjectSchema<ObjectShape, false>,
TSchema extends object = EffectiveWorkspaceObjects,
>(config: ActionConfig<TInput, TOutput, TSchema>): ActionDefinition;
type ActionExposure = "process" | "agent";
type ActionEffect = "read" | "write";
interface ActionConfig<
TInput extends ObjectSchema<ObjectShape, false> = ObjectSchema<ObjectShape, false>,
TOutput extends ObjectSchema<ObjectShape, false> = ObjectSchema<ObjectShape, false>,
TSchema extends object = EffectiveWorkspaceObjects,
> {
readonly key: string;
readonly label: string;
readonly description?: string;
readonly exposeTo: readonly ActionExposure[];
readonly effect: ActionEffect;
readonly input: TInput;
readonly output: TOutput;
readonly timeoutMs?: number;
readonly handler: ActionHandler<TInput, TOutput, TSchema>;
}
defineAction kiểm tra cấu hình ngay và ném ValidationError khi có vi phạm, khi
có key cấu hình lạ hoặc khi handler không phải function; khi key đã hợp lệ,
details.actionKey và details.option cho biết action và tuỳ chọn bị sai. Hàm trả về một ActionDefinition
đã freeze. Kiểu của tham số input và kiểu trả về của handler được suy ra từ schema
input và output.
| Tuỳ chọn | Bắt buộc | Quy tắc và ý nghĩa |
|---|---|---|
key |
Có | Định danh action trong project: một chữ thường, theo sau tối đa 63 chữ thường, chữ số hoặc _. Duy nhất trong project. Giữ ổn định giữa các version: node Process và tool của agent dùng key cũ sẽ không tìm thấy action nữa. |
label |
Có | Tên hiển thị trong Process Builder và AI Agent Builder, từ 1 đến 100 ký tự sau khi trim. |
description |
Với agent | Tối đa 1000 ký tự sau khi trim. Bắt buộc khi exposeTo có "agent": AI Agent đọc mô tả này để quyết định khi nào gọi tool. |
exposeTo |
Có | Danh sách khác rỗng gồm "process" và "agent", không trùng: nơi action được dùng. |
effect |
Có | "read": action chạy chỉ đọc, mọi thao tác ghi đều bị từ chối bằng PermissionDeniedError. "write": action được ghi; tool của AI Agent cho action này mặc định cần người phê duyệt. |
input |
Có | Schema của input, tạo bằng s.object. |
output |
Có | Schema của output, tạo bằng s.object. |
timeoutMs |
Không | Số nguyên từ 1000 đến 8000; mặc định 8000. Giới hạn thời gian của một lần gọi. |
handler |
Có | (context, input) => output, đồng bộ hoặc bất đồng bộ. |
s
s tạo schema vừa là kiểu TypeScript vừa là JSON Schema. Action dùng nó để khai báo
input và output, và agents.start dùng nó cho resultSchema (xem
Process và AI Agent).
const s: {
string(options?: { minLength?: number; maxLength?: number }): Schema<string>;
enum<const Values extends readonly string[]>(values: Values): Schema<Values[number]>;
number(options?: { minimum?: number; maximum?: number }): Schema<number>;
integer(options?: { minimum?: number; maximum?: number }): Schema<number>;
boolean(): Schema<boolean>;
date(): Schema<string>;
dateTime(): Schema<number>;
recordId(objectSlug: string): Schema<CogoverRecordId>;
array<Item extends Schema<unknown, false>>(item: Item, options?: { maxItems?: number }): Schema<InferSchema<Item>[]>;
object<Shape extends ObjectShape>(shape: Shape): ObjectSchema<Shape>;
};
interface Schema<T = unknown, Optional extends boolean = false> {
optional(): Schema<T, true>;
describe(text: string): Schema<T, Optional>;
toJSON(): JsonSchema;
}
interface ObjectSchema<Shape extends ObjectShape = ObjectShape, Optional extends boolean = false>
extends Schema</* kiểu object của Shape */, Optional> {
optional(): ObjectSchema<Shape, true>;
describe(text: string): ObjectSchema<Shape, Optional>;
}
type ObjectShape = { readonly [property: string]: Schema<unknown, boolean> };
type InferSchema<T> = T extends Schema<infer Value, boolean> ? Value : never;
interface JsonSchema {
readonly [keyword: string]: null | boolean | number | string | readonly unknown[] | JsonSchema;
}| Builder | Giá trị | JSON Schema | Kiểu dữ liệu Process |
|---|---|---|---|
s.string({ minLength?, maxLength? }) |
string |
{"type": "string"} |
Text |
s.enum(["A", "B"]) |
một trong các chuỗi đã liệt kê | {"type": "string", "enum": [...]} |
Text |
s.number({ minimum?, maximum? }) |
number hữu hạn |
{"type": "number"} |
Number |
s.integer({ minimum?, maximum? }) |
số nguyên an toàn | {"type": "integer"} |
Number |
s.boolean() |
boolean |
{"type": "boolean"} |
Boolean |
s.date() |
chuỗi YYYY-MM-DD |
{"type": "string", "format": "date"} |
Date |
s.dateTime() |
Unix millisecond, như field date_time |
{"type": "integer", "format": "x-epoch-ms"} |
Date time |
s.recordId("lead") |
ID của một record thuộc Object, kiểu CogoverRecordId |
{"type": "string", "x-cogover-object": "lead"} |
Text |
s.array(item, { maxItems? }) |
danh sách giá trị item |
{"type": "array", "items": {...}} |
danh sách theo kiểu của item |
s.object({ ... }) |
object có đúng các property này | {"type": "object", "properties": {...}, "required": [...], "additionalProperties": false} |
Record, mỗi property là một field con |
.optional() và .describe(text) trả về node mới.
.optional() cho phép bỏ property khỏi object chứa nó (không có trong required);
.describe(text) thêm description từ 1 đến 500 ký tự, lưu sau khi trim, được
Process Builder hiển thị và AI Agent đọc.
s.string: maxLength từ 1 đến 100000, mặc định 10000; minLength từ 0 đến
maxLength. Không có option pattern: kiểm tra định dạng, ví dụ mã hay địa chỉ email,
trong handler và ném ValidationError khi không khớp.
s.enum: từ 1 đến 100 chuỗi khác nhau, mỗi chuỗi từ 1 đến 200 ký tự.s.number và s.integer: minimum và maximum là số hữu hạn (số nguyên an toàn
với s.integer), và minimum không lớn hơn maximum.
s.recordId: slug của Object bắt đầu bằng chữ cái hoặc _ và có tối đa 128 chữ
cái, chữ số hoặc _. Giá trị có kiểu CogoverRecordId, nên handler truyền thẳng vào
records.get hoặc records.update mà không cần ép kiểu, và property output tạo bằng
nó nhận id của record mà records API trả về. Lúc chạy nó chỉ là chuỗi: Cogover
không kiểm tra record có tồn tại.
s.array: item không được optional; maxItems từ 1 đến 10000, mặc định 1000.s.object: tối đa 50 property, mỗi tên có dạng như leadId: một chữ cái, theo sau
tối đa 63 chữ cái, chữ số hoặc _. Property giữ thứ tự khai báo.
input và output của action, và resultSchema của agent, là một s.object không
optional, lồng object và array tối đa 3 cấp (object gốc là cấp thứ nhất). JSON Schema
của input và output của một action cộng lại, và schema kết quả của agent, tối đa
16384 byte UTF-8 khi mã hoá JSON.
s tạo mới là schema: object JSON Schema viết tay bị từ chối bằng
ValidationError. toJSON(), và do đó JSON.stringify(node), trả về JSON Schema
của node.
Schema là node schema mô tả giá trị của một kiểu TypeScript, ObjectSchema là node
tạo bằng s.object, và ObjectShape là bảng ánh xạ tên property sang node mà
s.object nhận. InferSchema cho kiểu TypeScript của một node, và JsonSchema là
JSON Schema dưới dạng dữ liệu JSON thuần:
const leadInput = s.object({ leadId: s.recordId("lead"), note: s.string().optional() });
type LeadInput = InferSchema<typeof leadInput>; // { leadId: string; note?: string }ActionDefinition và ActionManifestinterface ActionManifest {
readonly key: string;
readonly label: string;
readonly description: string;
readonly exposeTo: readonly ActionExposure[];
readonly effect: ActionEffect;
readonly timeoutMs: number;
readonly inputSchema: JsonSchema;
readonly outputSchema: JsonSchema;
}
interface ActionDefinition {
readonly key: string;
readonly config: ActionManifest;
readonly __cogoverActionHandler: (inputJson: string) => Promise<string>;
}
config là cấu hình đã chuẩn hoá dưới dạng JSON thuần đã deep-freeze, đã áp mọi giá
trị mặc định: description là "" khi không khai báo, timeoutMs là 8000 nếu
không đặt, label và description đã trim. Đây là nội dung Process Builder và AI
Agent Builder hiển thị. Nó tách khỏi object đã truyền cho defineAction.
__cogoverActionHandler dành riêng cho Cogover; code của project không gọi nó.
type ActionHandler<TInput, TOutput, TSchema extends object = EffectiveWorkspaceObjects> = (
context: ActionContext<TSchema>,
input: InferSchema<TInput>,
) => InferSchema<TOutput> | Promise<InferSchema<TOutput>>;
interface ActionContext<TSchema extends object = EffectiveWorkspaceObjects> {
readonly action: ActionInfo;
readonly invocation: ActionInvocationContext;
readonly data: DataApi<TSchema>;
readonly schema: SchemaApi<TSchema>;
readonly log: ScriptLogger;
readonly state: ProjectState;
readonly locks: DistributedLocks;
readonly push: PushApi<TSchema>;
readonly notifications: NotificationsApi;
readonly email: EmailApi<TSchema>;
readonly jobs: JobsApi;
readonly secrets: SecretsApi;
readonly crypto: CryptoApi;
readonly org: OrgApi;
readonly limits: LimitsApi;
readonly processes: ProcessesApi;
readonly agents: AgentsApi;
}
interface ActionInfo {
readonly key: string;
readonly runId: string;
readonly effect: ActionEffect;
}
type ActionInvocationContext = (UserInvocationContext | SystemInvocationContext) & {
readonly executionIdentity: "user" | "system";
readonly source: ActionSource;
};
type ActionSource = ProcessActionSource | AgentActionSource;
interface ProcessActionSource {
readonly type: "process";
readonly processId: string;
readonly processInfoId: string;
readonly instanceId: string;
readonly nodeId: string;
readonly runAs: ProcessActionRunAs | null;
}
type ProcessActionRunAs = "PROCESS_STARTER" | "PERSONNEL" | "SYSTEM";
interface AgentActionSource {
readonly type: "agent";
readonly agentId: string;
readonly sessionId: string | null;
readonly runId: string | null;
readonly interactive: boolean;
readonly initiatorPersonnelId: string | null;
}
data, schema, log, state, locks, push, notifications, email, jobs,
secrets, crypto, org và limits là chính các API mà script nhận được, còn
processes và agents được mô tả ở Process và AI Agent.
Action context không có input, request hay response: input là tham số thứ hai
của handler.
action.key và action.effect lặp lại cấu hình. action.runId định danh lần gọi
và giữ nguyên khi bên gọi gửi lại chính lần gọi đó (ví dụ node Process thử lại sau
lỗi mạng); lần chạy mới của node hoặc lần gọi tool mới của agent có runId mới.
Dùng nó làm idempotency key cho lời gọi ra ngoài và cho jobs.enqueue hoặc
processes.start.
invocation là context invocation của người dùng hoặc system (xem
Danh tính invocation và workspace hiện tại)
cộng thêm executionIdentity, lặp lại invocation.identity, và source.
invocation.source cho biết bên gọi. ProcessActionSource cho biết Process
(processId), định nghĩa Process (processInfoId), lượt chạy (instanceId), node
BPMN (nodeId) và runAs, giá trị ProcessActionRunAs mà node dùng để chọn danh tính
chạy action: "PROCESS_STARTER" (người khởi tạo Process), "PERSONNEL" (nhân sự do node
chỉ định, có thể lấy từ dữ liệu của Process) hoặc "SYSTEM"; là null khi bên gọi không
cho biết. AgentActionSource cho biết agent, phiên chat và lượt chạy của nó (mỗi giá
trị là null khi agent không được khởi chạy theo cách đó), interactive, bằng true
khi có người đang chat với agent, và initiatorPersonnelId, ID nhân sự của người đang
chat với agent hoặc người khởi chạy lượt chạy agent (null khi không có).
initiatorPersonnelId trong handler (ví dụ bằng
data.asUser, identity policy phải cho phép) và từ chối nếu không. Với
runAs: "PERSONNEL", Process có thể đã lấy nhân sự từ dữ liệu do người dùng hoặc hệ
thống bên ngoài cung cấp: đừng coi đó là bằng chứng ai đã yêu cầu.
input trước khi handler chạy. Input không hợp
lệ thì handler không chạy: bên gọi nhận INPUT_INVALID kèm đường dẫn JSON Pointer và
thông báo cho từng lỗi (tối đa 20). Vì vậy handler luôn nhận đúng các property đã
khai báo, với giá trị đúng kiểu.
output sau khi handler
trả về: giá trị không phải dữ liệu JSON, hoặc không khớp schema, được báo cho bên gọi
là OUTPUT_INVALID, và các thao tác ghi handler đã làm không được hoàn tác. Hãy trả
về một object; undefined không phải output hợp lệ.
found trong ví dụ trên, thay vì ném lỗi.
invocation.user mô tả người đó, data.object() dùng quyền của họ, và Cogover kiểm
tra trước rằng người đó vẫn là thành viên đang hoạt động của Workspace. Khi không có
người dùng, invocation.identity là "system", và lời gọi bị từ chối trước khi chạy
trừ khi identity policy đã duyệt của version đang active đặt
allowInternalSystem: true. data.asUser() và data.asSystem() tuân theo identity
policy như thông thường. Khi version đang active chưa có identity policy được duyệt,
action chạy giống route HTTP: lời gọi có người dùng chạy với quyền riêng của người đó
và không có quyền nào của policy (data.asSystem(), data.asUser(), email.send(),
fetch() tới port khác, processes.start() và agents.start() bị từ chối), còn lời
gọi không có người dùng bị từ chối trước khi chạy.
"read" được đọc record, schema và state, dùng org,
email.senders và crypto trừ khi dùng key { secret }, gửi fetch với method GET
hoặc HEAD không kèm credential, và gọi processes.get và agents.get. Mọi lời gọi khác,
như ghi record, locks, push, notifications.send, email.send, jobs.enqueue,
secrets.get, processes.start hoặc agents.start, ném PermissionDeniedError với
thông báo This action is declared read-only.
timeoutMs, tính từ lúc handler bắt đầu. Vượt thời gian sẽ kết
thúc lần gọi với TIMEOUT; handler không bắt được lỗi này.
runId khi
không nhận được câu trả lời. Khi đó, trong 7 ngày, Cogover trả kết quả đã lưu của lần
gọi đã xong mà không chạy lại handler, và từ chối cùng runId với input khác. Nếu
Cogover bị gián đoạn trong lúc handler chạy, lần thử lại sau có thể chạy lại handler,
nên hãy viết action có ghi dữ liệu theo kiểu idempotent, ví dụ dùng action.runId.
MAX_HOP_EXCEEDED trước khi chạy.
"process".
Node điền từng property cấp một của input từ dữ liệu của Process; property mà node
để trống sẽ không được gửi, nên hãy khai báo nó bằng .optional() trừ khi luôn cần.
Các node phía sau đọc output của node: status (COMPLETED hoặc FAILED), result
(output của action, mỗi property của output là một field con), errorCode,
errorMessage, runId và durationMs. Lời gọi thất bại sẽ dừng lượt chạy Process
trừ khi node được đặt tiếp tục khi lỗi.
"agent".
Agent thấy mô tả và schema input của action, kể cả các đoạn describe, và tự sinh
input; Cogover kiểm tra input đó như mọi input khác. Agent nhận output dưới dạng
JSON, hoặc mã lỗi và thông báo. Tool cho action "write" mặc định cần người phê
duyệt; cấu hình của agent có thể đổi điều này.
Lời gọi đã chạy báo COMPLETED kèm output, hoặc FAILED với một trong các mã sau:
| Mã | Ý nghĩa |
|---|---|
INPUT_INVALID |
Input không khớp schema input; handler không chạy. details.errors liệt kê path và message của từng lỗi. |
OUTPUT_INVALID |
Handler trả về giá trị không phải dữ liệu JSON hoặc không khớp schema output. |
SCRIPT_ERROR |
Handler ném lỗi SDK. details.scriptErrorCode là mã của lỗi đó, như VALIDATION_ERROR hoặc NOT_FOUND, kèm cùng thông báo an toàn mà HTTP route trả cho mã đó. Mọi lỗi khác được báo với thông báo The action handler failed. |
TIMEOUT |
Lời gọi vượt timeoutMs; details.timeoutMs là giới hạn đã áp dụng. |
RATE_LIMITED |
Lời gọi vượt một ngân sách của lần thực thi; details.budget mô tả ngân sách đó. |
SCRIPT_ERROR với details.reason là "RESOURCE_LIMIT_EXCEEDED" |
Handler dùng hết một giới hạn tài nguyên của sandbox: số câu lệnh, bộ nhớ hoặc stack. |
PERMISSION_DENIED |
Handler để lọt PermissionDeniedError, ví dụ khi ghi trong action "read". Trong lúc activate version mới, lời gọi có thể thất bại trong chốc lát với details.reason là "PROJECT_POLICY_NOT_APPROVED" trước khi chạy; lần gửi lại của bên gọi sẽ chạy. |
INTERNAL_ERROR |
Lỗi khác. |
Lời gọi cũng có thể bị từ chối trước khi action chạy: không tìm thấy action trong
version đang active hoặc action không mở cho bên gọi đó, người mà action sẽ chạy dưới
danh tính không còn là thành viên đang hoạt động, lời gọi không có người dùng không
được identity policy cho phép, ngân sách tự động hoá đã hết, cùng action.runId đã được
dùng với input khác hoặc bởi bên gọi hay danh tính khác, hoặc Cogover tạm thời không
sẵn sàng (bên gọi sẽ thử lại).

blog.blogs_related
Bắt đầu đổi mới phương thức vận hành và tự chủ hệ thống quản trị công việc của bạn
CÔNG TY CỔ PHẦN STRINGEE
Văn phòng