@opencxh/domain 1.274.0 → 1.276.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/entities/activity/types.d.ts +5 -1
- package/dist/entities/ai-account/capabilities.d.ts +4 -0
- package/dist/entities/ai-account/index.d.ts +3 -0
- package/dist/entities/ai-account/modality.d.ts +24 -0
- package/dist/entities/ai-account/modality.test.d.ts +1 -0
- package/dist/entities/ai-account/routing.d.ts +71 -0
- package/dist/entities/ai-account/routing.test.d.ts +1 -0
- package/dist/entities/ai-account/system-one.d.ts +109 -0
- package/dist/entities/ai-account/system-one.test.d.ts +1 -0
- package/dist/entities/ai-account/types.d.ts +12 -5
- package/dist/entities/ai-account/vendors.d.ts +2 -7
- package/dist/entities/ai-message/types.d.ts +9 -0
- package/dist/entities/ai-settings/types.d.ts +9 -0
- package/dist/entities/approval/decide-each.d.ts +16 -0
- package/dist/entities/approval/decide-each.test.d.ts +1 -0
- package/dist/entities/approval/index.d.ts +1 -0
- package/dist/entities/eval/index.d.ts +2 -0
- package/dist/entities/eval/runner.d.ts +14 -0
- package/dist/entities/eval/runner.test.d.ts +1 -0
- package/dist/entities/eval/types.d.ts +46 -0
- package/dist/entities/inbox/types.d.ts +4 -4
- package/dist/entities/interaction/attention.d.ts +12 -13
- package/dist/entities/kb/index.d.ts +1 -0
- package/dist/entities/kb/keys.d.ts +2 -0
- package/dist/entities/playbook/actor.d.ts +1 -12
- package/dist/entities/playbook/approval.d.ts +14 -0
- package/dist/entities/playbook/approval.test.d.ts +1 -0
- package/dist/entities/playbook/autonomy.d.ts +25 -0
- package/dist/entities/playbook/autonomy.test.d.ts +1 -0
- package/dist/entities/playbook/index.d.ts +2 -0
- package/dist/entities/playbook/labels.d.ts +1 -3
- package/dist/entities/playbook/types.d.ts +30 -38
- package/dist/entities/reaction/types.d.ts +6 -1
- package/dist/entities/rule/bind.d.ts +23 -0
- package/dist/entities/rule/bind.test.d.ts +1 -0
- package/dist/entities/rule/cron.d.ts +40 -0
- package/dist/entities/rule/cron.test.d.ts +1 -0
- package/dist/entities/rule/eval.d.ts +31 -0
- package/dist/entities/rule/index.d.ts +4 -0
- package/dist/entities/rule/match.d.ts +8 -0
- package/dist/entities/rule/outcome.d.ts +44 -0
- package/dist/entities/rule/outcome.test.d.ts +1 -0
- package/dist/entities/rule/trigger-source.d.ts +4 -4
- package/dist/entities/rule/types.d.ts +91 -4
- package/dist/entities/scope/actor.d.ts +0 -5
- package/dist/entities/sla/types.d.ts +1 -1
- package/dist/entities/webhook/types.d.ts +7 -1
- package/dist/index.cjs +20 -20
- package/dist/index.d.ts +1 -0
- package/dist/index.js +2061 -1700
- package/dist/platform/ai-tools.d.ts +34 -15
- package/dist/platform/permission.d.ts +2 -2
- package/dist/platform/resource-source.d.ts +19 -0
- package/dist/platform/tool-verdict.d.ts +22 -9
- package/package.json +1 -1
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { ActingIdentity } from '../../platform/identity';
|
|
2
2
|
import { ResourceRef } from '../../platform/resource';
|
|
3
|
+
import { AssignmentControl } from '../assignment/types';
|
|
4
|
+
import { Trigger } from '../rule/types';
|
|
3
5
|
import { OwnerScope } from '../scope/types';
|
|
4
6
|
/**
|
|
5
7
|
* Playbooks — event-driven automation with AI steps. See docs/PLAYBOOKS.md.
|
|
@@ -7,41 +9,18 @@ import { OwnerScope } from '../scope/types';
|
|
|
7
9
|
* Two execution forms, one contract: see {@link Decider}. Every step type in
|
|
8
10
|
* {@link Step} is executed; there is no kind the executor refuses.
|
|
9
11
|
*/
|
|
10
|
-
export type Autonomy = "suggest" | "auto";
|
|
11
|
-
export type PlaybookTrigger = {
|
|
12
|
-
kind: "activity";
|
|
13
|
-
activityTypes: string[];
|
|
14
|
-
/** Channel kinds: "mail" | "chat" | "voice" (derived from the activity type). */
|
|
15
|
-
channels?: string[];
|
|
16
|
-
/** Specific channel ids (e.g. per mailbox). Matched against activity.channelId. */
|
|
17
|
-
channelIds?: string[];
|
|
18
|
-
filter?: unknown;
|
|
19
|
-
/**
|
|
20
|
-
* Wait this many milliseconds, then run **once** for the messages in that window
|
|
21
|
-
* instead of once per message. The run executes on the **newest** message; the ones
|
|
22
|
-
* in between are already covered by the conversation lookup step.
|
|
23
|
-
*
|
|
24
|
-
* Deliberately on the trigger, not in the memory layer: coalescing is a property of
|
|
25
|
-
* "how often do I want to react", not of what you remember.
|
|
26
|
-
*/
|
|
27
|
-
debounceMs?: number;
|
|
28
|
-
} | {
|
|
29
|
-
kind: "manual";
|
|
30
|
-
}
|
|
31
12
|
/**
|
|
32
|
-
*
|
|
33
|
-
*
|
|
34
|
-
* Not a new mechanism: an assignment is already an activity, so the same gate delivers it.
|
|
35
|
-
* The question the engine cannot answer itself — *is this for MY agent?* — is answered by
|
|
36
|
-
* `Playbook.agentId`, not here: one place names the agent, because that field also decides
|
|
37
|
-
* who the run acts as. Which kinds of work can be assigned is **declared** by the app that
|
|
38
|
-
* owns them (`assignment-source` federation, see `./assignment.ts`), hence a loose id in
|
|
39
|
-
* `targetKind` instead of a union here.
|
|
13
|
+
* How much a run may do on its own: ask before every write, ask only before a write that leaves
|
|
14
|
+
* the organisation, or ask nothing. Ordered from most to least careful.
|
|
40
15
|
*/
|
|
41
|
-
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
16
|
+
export type Autonomy = "suggest" | "outward" | "auto";
|
|
17
|
+
/**
|
|
18
|
+
* The arms of {@link Trigger} the playbook executor runs: an activity, a person, an assignment.
|
|
19
|
+
* One vocabulary; each executor names the part it runs (rules: `event` and `schedule`).
|
|
20
|
+
*/
|
|
21
|
+
export type PlaybookTrigger = Extract<Trigger, {
|
|
22
|
+
kind: "activity" | "manual" | "assignment";
|
|
23
|
+
}>;
|
|
45
24
|
export interface Guardrails {
|
|
46
25
|
maxSteps?: number;
|
|
47
26
|
maxToolCalls?: number;
|
|
@@ -96,14 +75,14 @@ export interface ForEachStep<S = Step> {
|
|
|
96
75
|
}
|
|
97
76
|
export interface LookupStep {
|
|
98
77
|
type: "lookup";
|
|
99
|
-
/**
|
|
78
|
+
/** The read tool, `<app>__<name>`. */
|
|
100
79
|
source: string;
|
|
101
80
|
filter?: unknown;
|
|
102
81
|
as: string;
|
|
103
82
|
}
|
|
104
83
|
export interface ActionStep {
|
|
105
84
|
type: "action";
|
|
106
|
-
/**
|
|
85
|
+
/** The write tool, `<app>__<name>`. */
|
|
107
86
|
action: string;
|
|
108
87
|
params: Record<string, unknown>;
|
|
109
88
|
autonomy?: Autonomy;
|
|
@@ -120,11 +99,11 @@ export interface ActionStep {
|
|
|
120
99
|
/**
|
|
121
100
|
* Wait until this task is done. Counterpart of `wait-for-reply`, for the support-procedure
|
|
122
101
|
* case "a colleague must do something: create a task and wait for it". `taskId` is an
|
|
123
|
-
* ordinary reference, e.g.
|
|
102
|
+
* ordinary reference, e.g. `{{newTask.id}}` after a `create_task` with an `as`.
|
|
124
103
|
*/
|
|
125
104
|
export interface WaitForTaskStep {
|
|
126
105
|
type: "wait-for-task";
|
|
127
|
-
/**
|
|
106
|
+
/** `{{ref}}` or literal id of the task to wait on. */
|
|
128
107
|
taskId: string;
|
|
129
108
|
}
|
|
130
109
|
/**
|
|
@@ -145,7 +124,7 @@ export interface ClassifyStep {
|
|
|
145
124
|
fields?: unknown;
|
|
146
125
|
instruction?: string;
|
|
147
126
|
/**
|
|
148
|
-
* Which text is classified, as a
|
|
127
|
+
* Which text is classified, as a `{{ref}}` into the run vars. Default `{{trigger.text}}`.
|
|
149
128
|
* Needed when the trigger carries no text: an `INTERACTION_STATUS_CHANGED` activity has
|
|
150
129
|
* only `{ fromStatus, toStatus }`, so `trigger.text` is empty. With `input: "$thread"` the
|
|
151
130
|
* step reads what an earlier `lookup` fetched.
|
|
@@ -217,6 +196,8 @@ export interface ParallelStep<S = Step> {
|
|
|
217
196
|
onBranchError?: "fail" | "continue";
|
|
218
197
|
}
|
|
219
198
|
export type Step = ConditionStep | ForEachStep | ParallelStep | LookupStep | ActionStep | ClassifyStep | GenerateStep | AgentStep | WaitForReplyStep | WaitForTaskStep;
|
|
199
|
+
/** Every step type, once. The `satisfies` pair fails to compile when a type is added or dropped. */
|
|
200
|
+
export declare const STEP_TYPES: readonly ["condition", "for-each", "parallel", "lookup", "action", "classify", "generate", "agent", "wait-for-reply", "wait-for-task"];
|
|
220
201
|
/**
|
|
221
202
|
* A **procedure**: how a scenario is handled, as prose with guardrails. The second execution
|
|
222
203
|
* form next to `Step[]`; {@link AgentStep} is its inside-a-flow variant (it *contains* one).
|
|
@@ -477,6 +458,11 @@ export interface PlaybookRun {
|
|
|
477
458
|
subjectKind?: string;
|
|
478
459
|
subjectId?: string;
|
|
479
460
|
status: RunStatus;
|
|
461
|
+
/**
|
|
462
|
+
* The autonomy this run started with ({@link resolveAutonomy}), frozen: a resume after an
|
|
463
|
+
* approval runs at the same setting it parked with. Absent on runs from before this field.
|
|
464
|
+
*/
|
|
465
|
+
autonomy?: Autonomy;
|
|
480
466
|
/**
|
|
481
467
|
* The cause: which signal started this run, plus the input as it looked **then**.
|
|
482
468
|
*
|
|
@@ -522,6 +508,12 @@ export interface PlaybookRun {
|
|
|
522
508
|
}[] | null;
|
|
523
509
|
/** The AI_ACTION_PROPOSED activity created for the current pending batch (for resolve on approve/reject). */
|
|
524
510
|
proposalActivityId?: string | null;
|
|
511
|
+
/** The approval request a parked run waits on (`runApprovalKey`). */
|
|
512
|
+
approvalRequestId?: string | null;
|
|
513
|
+
/** Every request this park waits on, one per distinct act; resumes when all are approved. */
|
|
514
|
+
approvalRequestIds?: string[];
|
|
515
|
+
/** The move an assignment turn chose before it parked; restored when the turn resumes. */
|
|
516
|
+
turnControl?: AssignmentControl | null;
|
|
525
517
|
/**
|
|
526
518
|
* What this run waits for, when `status === "waiting"`. `null` when cleared (the store
|
|
527
519
|
* writes `null`), absent on a run that is not waiting. A new wait reason costs a member in
|
|
@@ -67,9 +67,14 @@ export interface ReactionChange {
|
|
|
67
67
|
actor: ReactionActor;
|
|
68
68
|
/** `true` put it there, `false` took it away. */
|
|
69
69
|
on: boolean;
|
|
70
|
+
/**
|
|
71
|
+
* The signed-in user who clicked. Absent when an app wrote it (a provider sync carrying a
|
|
72
|
+
* Teams emoji across), so a listener that syncs out never echoes a mirrored reaction back.
|
|
73
|
+
*/
|
|
74
|
+
byUser?: string;
|
|
70
75
|
}
|
|
71
76
|
/**
|
|
72
|
-
* The `reactions:changed` event, as apps receive it (`
|
|
77
|
+
* The `reactions:changed` event, as apps receive it (`context:reactions:changed`).
|
|
73
78
|
*
|
|
74
79
|
* Carries **both** the resulting chip set and the delta, because the two consumers need
|
|
75
80
|
* different things. A feed re-renders from `reactions` — a client that only got deltas and
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* One variable language for rules and playbooks: `{{path}}`.
|
|
3
|
+
*
|
|
4
|
+
* A value that is *only* a reference keeps the referenced value's type (`"{{subject.id}}"` → the
|
|
5
|
+
* id, a number stays a number); a reference inside longer text is stringified, objects as JSON.
|
|
6
|
+
* `{ "var": "path" }` in a condition is json-logic's own and not handled here.
|
|
7
|
+
*
|
|
8
|
+
* What an unknown reference becomes is the executor's call:
|
|
9
|
+
* - `"empty"` (rules): `""` — the same "empty is not an instruction" the tools already read;
|
|
10
|
+
* - `"throw"` (playbooks): an error when the path's **root** is not there (a typo, or a step that
|
|
11
|
+
* never ran). A known root with an empty leaf stays empty: a chat message has no `subject`, and
|
|
12
|
+
* that is not a mistake.
|
|
13
|
+
*/
|
|
14
|
+
export interface BindOptions {
|
|
15
|
+
onMissing?: "empty" | "throw";
|
|
16
|
+
/** Where the value sits, for the error message. */
|
|
17
|
+
where?: string;
|
|
18
|
+
}
|
|
19
|
+
export declare class UnresolvedRefError extends Error {
|
|
20
|
+
constructor(message: string);
|
|
21
|
+
}
|
|
22
|
+
export declare function bindParams(params: Record<string, unknown>, context: Record<string, unknown>, options?: BindOptions): Record<string, unknown>;
|
|
23
|
+
export declare function bindValue(value: unknown, context: Record<string, unknown>, options?: BindOptions): unknown;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Does this cron expression name this minute?
|
|
3
|
+
*
|
|
4
|
+
* Five fields, standard order: `minute hour day-of-month month day-of-week`. Each field is `*`,
|
|
5
|
+
* a number, a range (`a-b`), a step (`*` slash `n`), or a comma-separated list of those.
|
|
6
|
+
*
|
|
7
|
+
* **Hand-written and not a library.** It is thirty lines, it has to run inside QuickJS, and the
|
|
8
|
+
* alternative is a dependency in `packages/domain` — which carries none, on purpose. What it
|
|
9
|
+
* deliberately does **not** support is the part of cron nobody writes by hand: `L`, `W`, `#`,
|
|
10
|
+
* named months and weekdays, and seconds. An expression using one of those is refused rather
|
|
11
|
+
* than quietly matching nothing, which is the failure mode a scheduled rule cannot afford — a
|
|
12
|
+
* rule that never fires looks exactly like a rule whose condition is never true.
|
|
13
|
+
*/
|
|
14
|
+
export interface ParsedCron {
|
|
15
|
+
minute: Set<number>;
|
|
16
|
+
hour: Set<number>;
|
|
17
|
+
dayOfMonth: Set<number>;
|
|
18
|
+
month: Set<number>;
|
|
19
|
+
dayOfWeek: Set<number>;
|
|
20
|
+
/** True when either day field was left as `*` — see {@link cronMatches}. */
|
|
21
|
+
anyDayOfMonth: boolean;
|
|
22
|
+
anyDayOfWeek: boolean;
|
|
23
|
+
}
|
|
24
|
+
/** `null` when the expression is not one this matcher can honour. */
|
|
25
|
+
export declare function parseCron(expression: string): ParsedCron | null;
|
|
26
|
+
/** Is this expression one this matcher can honour at all? */
|
|
27
|
+
export declare function isSupportedCron(expression: string): boolean;
|
|
28
|
+
/**
|
|
29
|
+
* Does this expression name the minute `date` falls in?
|
|
30
|
+
*
|
|
31
|
+
* The day rule is cron's one genuine oddity and it is kept: when **both** day fields are
|
|
32
|
+
* restricted they are OR-ed, not AND-ed — `0 9 1 * 1` means "the first of the month, and every
|
|
33
|
+
* Monday", not "a Monday that is the first". Every cron implementation does this, and quietly
|
|
34
|
+
* doing the intuitive thing instead would make a rule fire on a handful of days a year.
|
|
35
|
+
*
|
|
36
|
+
* Evaluated in **UTC**, because that is what the tick that calls it runs in. A rule author who
|
|
37
|
+
* means nine in the morning in Amsterdam writes the UTC hour; naming a timezone per rule is a
|
|
38
|
+
* field this type does not have yet, and guessing the organisation's is worse than the omission.
|
|
39
|
+
*/
|
|
40
|
+
export declare function cronMatches(expression: string, date: Date): boolean;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { EvalCase, EvalCaseResult } from '../eval/types';
|
|
2
|
+
import { Rule, RuleEvent } from './types';
|
|
3
|
+
/**
|
|
4
|
+
* The first eval suite, and the one that is free: **does this rule set do what its author said?**
|
|
5
|
+
*
|
|
6
|
+
* Deterministic, no model, no network — which is what makes it the suite that belongs in CI. The
|
|
7
|
+
* whole question is whether `matchRules` plus a condition produces the actions somebody expected,
|
|
8
|
+
* and every part of that is already pure.
|
|
9
|
+
*
|
|
10
|
+
* This is the check that would have caught the shipped starter template pointing at
|
|
11
|
+
* `subject.lastActivityPreview.text`: a field no conversation has, so the rule compared
|
|
12
|
+
* `undefined`, never fired, and said nothing about why. It was found by hand, once. A case says
|
|
13
|
+
* "this event, this rule, these actions" and it stays found.
|
|
14
|
+
*/
|
|
15
|
+
export interface RuleCaseInput {
|
|
16
|
+
event: RuleEvent;
|
|
17
|
+
rules: Rule[];
|
|
18
|
+
}
|
|
19
|
+
export interface RuleCaseExpected {
|
|
20
|
+
/** The actions that should fire, in order. An empty list means "this must not fire". */
|
|
21
|
+
actions: string[];
|
|
22
|
+
}
|
|
23
|
+
export type RuleEvalCase = EvalCase<RuleCaseInput, RuleCaseExpected>;
|
|
24
|
+
/**
|
|
25
|
+
* Judge one rule case.
|
|
26
|
+
*
|
|
27
|
+
* The evaluator is injected and the caller is expected to pass **the production one**. A suite
|
|
28
|
+
* with its own condition semantics measures its own semantics; the point is to measure what will
|
|
29
|
+
* actually run.
|
|
30
|
+
*/
|
|
31
|
+
export declare function judgeRuleCase(testCase: RuleEvalCase, evaluate: (condition: unknown, context: Record<string, unknown>) => boolean): EvalCaseResult;
|
|
@@ -1,4 +1,12 @@
|
|
|
1
1
|
import { Rule, RuleEvent } from './types';
|
|
2
|
+
/**
|
|
3
|
+
* The event name a clock tick carries.
|
|
4
|
+
*
|
|
5
|
+
* A reserved name and not a kind of its own, so the tick travels through the same matcher, the
|
|
6
|
+
* same guards and the same idempotency key as everything else. `@` cannot occur in an
|
|
7
|
+
* `<app>:<resource>:<verb>` name, so no app can ever deliver it by accident.
|
|
8
|
+
*/
|
|
9
|
+
export declare const SCHEDULE_EVENT = "@schedule";
|
|
2
10
|
/**
|
|
3
11
|
* How deep a chain of rules may go before it is refused.
|
|
4
12
|
*
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Is what a rule wrote still standing?
|
|
3
|
+
*
|
|
4
|
+
* The feedback loop's first real signal, and it needs no new write path: every run already
|
|
5
|
+
* carries `priors` — the previous value of each field it wrote, plus the params it wrote them
|
|
6
|
+
* with — because undo needs exactly that. Asking the same question an hour later turns it into a
|
|
7
|
+
* measurement.
|
|
8
|
+
*
|
|
9
|
+
* **Why this matters more than an approval rate.** The trust ladder promotes on *observed
|
|
10
|
+
* unchanged approval*, and approving is a ritual — people click yes. A correction is a real
|
|
11
|
+
* objection, and so is an undo. An automatic action that could be reversed in one click and never
|
|
12
|
+
* is, is a stronger promotion signal than any approval percentage; a burst of corrections is an
|
|
13
|
+
* immediate demotion signal.
|
|
14
|
+
*/
|
|
15
|
+
export type RunOutcome = "kept" | "corrected" | "undone" | "unknown";
|
|
16
|
+
/** The half of an action's prior this question needs. `before` is undo's business, not this one. */
|
|
17
|
+
export interface OutcomePrior {
|
|
18
|
+
action: string;
|
|
19
|
+
params: Record<string, unknown>;
|
|
20
|
+
/** Which params were content — a field the subject owns — rather than addressing. */
|
|
21
|
+
fields: string[];
|
|
22
|
+
/** Fields the tool reported writing with a value it chose itself (`AiToolResult.wrote`). */
|
|
23
|
+
wrote?: Record<string, unknown>;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Which fields no longer hold what the action put there.
|
|
27
|
+
*
|
|
28
|
+
* Shared with the undo, deliberately: "changed since" decides both whether an undo may run and
|
|
29
|
+
* whether this run counts as corrected, and two definitions of that would eventually disagree —
|
|
30
|
+
* with the undo overwriting somebody's change while the measurement called it kept.
|
|
31
|
+
*
|
|
32
|
+
* A field the subject does not report at all (`undefined`) is **not** drift. That is a reader
|
|
33
|
+
* that did not return the column, not a person who changed it, and counting it would turn every
|
|
34
|
+
* thin read into a wave of false corrections.
|
|
35
|
+
*/
|
|
36
|
+
export declare function driftedFields(prior: OutcomePrior, current: Record<string, unknown> | undefined): string[];
|
|
37
|
+
/**
|
|
38
|
+
* The outcome of one run.
|
|
39
|
+
*
|
|
40
|
+
* `unknown` when the subject cannot be read — it was deleted, or its kind has no reader. Kept
|
|
41
|
+
* apart from `kept` on purpose: "nobody objected" and "we could not look" are different facts,
|
|
42
|
+
* and folding the second into the first is how a trust ladder promotes on silence.
|
|
43
|
+
*/
|
|
44
|
+
export declare function runOutcome(priors: readonly OutcomePrior[], current: Record<string, unknown> | undefined, undoneAt?: number): RunOutcome;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { ToolCatalogEntry } from '../../platform/ai-tools';
|
|
2
|
+
import { RuleTemplate } from './types';
|
|
2
3
|
/**
|
|
3
4
|
* What kind of value sits at a field, so the builder knows which widget to draw and the
|
|
4
5
|
* comparison operators that make sense.
|
|
@@ -79,16 +80,15 @@ export interface EventDescriptor {
|
|
|
79
80
|
name: string;
|
|
80
81
|
/** What this event is, in the author's language: "Er komt een bericht binnen". */
|
|
81
82
|
label: string;
|
|
82
|
-
/**
|
|
83
|
-
* What the subject is. Also the key the undo reader uses to fetch current state, so a new
|
|
84
|
-
* subject kind needs a reader there before its runs can be undone.
|
|
85
|
-
*/
|
|
83
|
+
/** What the subject is, for the builder. The run itself records the subject's scope kind. */
|
|
86
84
|
subjectKind: string;
|
|
87
85
|
fields: EventField[];
|
|
88
86
|
}
|
|
89
87
|
/** What a `trigger-source` returns on `GET /provider/triggers/describe`. */
|
|
90
88
|
export interface TriggerSourceDescription {
|
|
91
89
|
events: EventDescriptor[];
|
|
90
|
+
/** Rules to start from, on this app's own events. */
|
|
91
|
+
templates?: RuleTemplate[];
|
|
92
92
|
}
|
|
93
93
|
/** An event plus the app that answered for it — the merged catalog a builder reads. */
|
|
94
94
|
export interface VocabularyEvent extends EventDescriptor {
|
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
import { OwnerScope } from '../scope/types';
|
|
2
|
+
import { OutcomePrior, RunOutcome } from './outcome';
|
|
2
3
|
/**
|
|
3
4
|
* When something starts. One vocabulary for every executor.
|
|
4
5
|
*
|
|
5
6
|
* There were five of these, and they had to agree without anything saying so: `PlaybookTrigger`,
|
|
6
7
|
* `webhook.triggers[]` (four fixed strings), the sync tick, and twenty-one hand-rolled cron jobs.
|
|
8
|
+
* Playbooks now run a subset of this union (`PlaybookTrigger`); webhooks store `event` names.
|
|
7
9
|
*
|
|
8
|
-
* **
|
|
10
|
+
* **No `stream` arm.** `stream` is deliberately absent. It existed once, as a field-less
|
|
9
11
|
* `{ kind: "stream" }` on `PlaybookTrigger`, and was removed with a reason that generalises:
|
|
10
12
|
* *"Definitions belong separated by their runtime, not by their editor."* A live read lane has no
|
|
11
13
|
* run, no lock and no gate, so it is not an automation with a trigger — it is its own thing
|
|
@@ -26,18 +28,42 @@ export type Trigger =
|
|
|
26
28
|
| {
|
|
27
29
|
kind: "manual";
|
|
28
30
|
}
|
|
29
|
-
/**
|
|
31
|
+
/**
|
|
32
|
+
* Work was handed to an agent. `targetKind` is declared by the app that owns that work
|
|
33
|
+
* (`assignment-source` federation, see `playbook/assignment.ts`); which agent is answered by
|
|
34
|
+
* `Playbook.agentId`, not here.
|
|
35
|
+
*/
|
|
30
36
|
| {
|
|
31
37
|
kind: "assignment";
|
|
32
38
|
targetKind: string;
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* A message appears: these activity types, optionally on these channel kinds or ids, passing
|
|
42
|
+
* `filter` (JSONLogic). Its own arm rather than an `event` with a filter, so the editor keeps
|
|
43
|
+
* its pickers instead of an expression.
|
|
44
|
+
*/
|
|
45
|
+
| {
|
|
46
|
+
kind: "activity";
|
|
47
|
+
activityTypes: string[];
|
|
48
|
+
/** Channel kinds: "mail" | "chat" | "voice" (derived from the activity type). */
|
|
49
|
+
channels?: string[];
|
|
50
|
+
/** Specific channel ids (e.g. per mailbox). Matched against activity.channelId. */
|
|
51
|
+
channelIds?: string[];
|
|
52
|
+
filter?: unknown;
|
|
53
|
+
/**
|
|
54
|
+
* Wait this many milliseconds, then run **once** for the messages in that window instead
|
|
55
|
+
* of once per message, on the newest one. Deliberately on the trigger: coalescing is
|
|
56
|
+
* "how often do I react", not what you remember.
|
|
57
|
+
*/
|
|
58
|
+
debounceMs?: number;
|
|
33
59
|
};
|
|
34
60
|
/**
|
|
35
61
|
* One thing a rule does.
|
|
36
62
|
*
|
|
37
|
-
* **`action` is
|
|
63
|
+
* **`action` is `<app>__<tool>`** — the action library is the tool federation, the
|
|
38
64
|
* same 62 descriptors the assistant uses, with the model taken out from in front. A second
|
|
39
65
|
* library would duplicate what a descriptor already carries: a JSON-Schema, a scope gate, an
|
|
40
|
-
* `effect` classification and an approval hook.
|
|
66
|
+
* `effect` classification and an approval hook. Playbooks spell it the same.
|
|
41
67
|
*/
|
|
42
68
|
export interface RuleAction {
|
|
43
69
|
action: string;
|
|
@@ -101,3 +127,64 @@ export interface RuleEvent {
|
|
|
101
127
|
/** How many rule hops produced this event. Absent = zero, a genuine first cause. */
|
|
102
128
|
depth?: number;
|
|
103
129
|
}
|
|
130
|
+
/** One line of a rule run log. `ok: false` is a step that did not land, not a rule that did not fire. */
|
|
131
|
+
export interface RuleTraceEntry {
|
|
132
|
+
ruleId: string;
|
|
133
|
+
action?: string;
|
|
134
|
+
ok: boolean;
|
|
135
|
+
note: string;
|
|
136
|
+
}
|
|
137
|
+
/**
|
|
138
|
+
* What one action changed, captured when it ran — the params it was called with, which of those
|
|
139
|
+
* were content (a field the subject owns) rather than addressing, and those fields as they were.
|
|
140
|
+
* A key missing from `before` held nothing.
|
|
141
|
+
*/
|
|
142
|
+
export interface ActionPrior extends OutcomePrior {
|
|
143
|
+
/** The rule that ran this action. Empty for an undo replay. */
|
|
144
|
+
ruleId?: string;
|
|
145
|
+
before: Record<string, unknown>;
|
|
146
|
+
}
|
|
147
|
+
/** What an owning app's write path posts after a rule acted: one rule, one event. */
|
|
148
|
+
export interface RuleRunRecord {
|
|
149
|
+
ruleId: string;
|
|
150
|
+
ruleName?: string;
|
|
151
|
+
eventName: string;
|
|
152
|
+
eventId: string;
|
|
153
|
+
subjectKind?: string;
|
|
154
|
+
subjectId?: string;
|
|
155
|
+
applied: number;
|
|
156
|
+
trace: RuleTraceEntry[];
|
|
157
|
+
priors: ActionPrior[];
|
|
158
|
+
}
|
|
159
|
+
/**
|
|
160
|
+
* One thing a rule did, once, as the run log stores it. Written only when something happened.
|
|
161
|
+
*
|
|
162
|
+
* `outcome` is stamped an hour after the fact; empty means "not judged yet", and `unknown` is
|
|
163
|
+
* deliberately not `kept` — "nobody objected" and "we could not look" are different facts.
|
|
164
|
+
*/
|
|
165
|
+
export interface RuleRun extends RuleRunRecord {
|
|
166
|
+
id: string;
|
|
167
|
+
undoneAt?: number;
|
|
168
|
+
undoneBy?: string;
|
|
169
|
+
outcome?: RunOutcome | "";
|
|
170
|
+
outcomeAt?: number;
|
|
171
|
+
createdAt?: number;
|
|
172
|
+
}
|
|
173
|
+
/**
|
|
174
|
+
* A rule an organisation can start from, declared by the app whose event it listens to.
|
|
175
|
+
*
|
|
176
|
+
* Data, not rows seeded on install: an organisation that never opens the builder should not
|
|
177
|
+
* acquire automation it did not ask for. Installing one is a normal create, so it lands disabled
|
|
178
|
+
* and passes the same save-time gate as anything a person writes.
|
|
179
|
+
*/
|
|
180
|
+
export interface RuleTemplate {
|
|
181
|
+
id: string;
|
|
182
|
+
name: string;
|
|
183
|
+
description: string;
|
|
184
|
+
/** What the author still has to fill in. Named, so the form can ask instead of guessing. */
|
|
185
|
+
blanks: {
|
|
186
|
+
path: string;
|
|
187
|
+
label: string;
|
|
188
|
+
}[];
|
|
189
|
+
rule: Pick<Rule, "trigger" | "when" | "then">;
|
|
190
|
+
}
|
|
@@ -7,10 +7,5 @@ import { OwnerScope } from './types';
|
|
|
7
7
|
* carries an id on one side and not on the other. This is that translation, and it is
|
|
8
8
|
* meant to be the only one: a hand-rolled `kind === "personal" ? "user" : …` at a call
|
|
9
9
|
* site is how two vocabularies for the same three rungs drift apart.
|
|
10
|
-
*
|
|
11
|
-
* It arrived as `playbookActor` in `entities/playbook/`, whose own docstring already
|
|
12
|
-
* claimed to be "the only place this belongs" — correct, but it read as
|
|
13
|
-
* playbook-specific while every scoped entity needs the same conversion. `playbookActor`
|
|
14
|
-
* stays as a name so the playbook call sites and their tests keep working.
|
|
15
10
|
*/
|
|
16
11
|
export declare function toActingIdentity(scope: OwnerScope): ActingIdentity;
|
|
@@ -50,7 +50,7 @@ export interface SlaTarget {
|
|
|
50
50
|
}
|
|
51
51
|
/**
|
|
52
52
|
* A named set of service targets. Assigned to a resource explicitly — from a workflow rule
|
|
53
|
-
* (`
|
|
53
|
+
* (`context__apply_sla`) or by hand — never matched automatically. That is why there is
|
|
54
54
|
* no `appliesTo` and no `order`: both only exist to drive a matcher nothing calls.
|
|
55
55
|
*/
|
|
56
56
|
export interface SlaProfile {
|
|
@@ -5,7 +5,13 @@
|
|
|
5
5
|
*/
|
|
6
6
|
export type WebhookMethod = "POST" | "PUT";
|
|
7
7
|
export type WebhookContentType = "json" | "multipart";
|
|
8
|
-
|
|
8
|
+
/**
|
|
9
|
+
* The bus events a webhook can listen to — full names, the same string as a rule's
|
|
10
|
+
* `Trigger.event.name`. Add one here and the automations app subscribes to it.
|
|
11
|
+
*/
|
|
12
|
+
export declare const WEBHOOK_EVENTS: readonly ["communication:interactions:created", "communication:interactions:updated", "communication:activities:created", "communication:activities:updated"];
|
|
13
|
+
export type WebhookTrigger = (typeof WEBHOOK_EVENTS)[number];
|
|
14
|
+
export declare function isWebhookEvent(value: unknown): value is WebhookTrigger;
|
|
9
15
|
export interface Webhook {
|
|
10
16
|
id: string;
|
|
11
17
|
organizationId: string;
|