@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
|
@@ -281,7 +281,7 @@ export type AIActionProposedPayload = {
|
|
|
281
281
|
runId: string;
|
|
282
282
|
playbookId?: string;
|
|
283
283
|
playbookName?: string;
|
|
284
|
-
/** The held actions, e.g. { action: "
|
|
284
|
+
/** The held actions, e.g. { action: "mcp__jira__create_issue", params }. */
|
|
285
285
|
actions: {
|
|
286
286
|
action: string;
|
|
287
287
|
params?: Record<string, unknown>;
|
|
@@ -289,6 +289,10 @@ export type AIActionProposedPayload = {
|
|
|
289
289
|
status: "pending" | "approved" | "rejected";
|
|
290
290
|
/** Who the proposal was made on behalf of, so the card can show "on behalf of Finance". */
|
|
291
291
|
actor?: ActingIdentity;
|
|
292
|
+
/** The approval request that decides it — the card decides through that, not through ai. */
|
|
293
|
+
approvalRequestId?: string;
|
|
294
|
+
/** Every request this proposal waits on, one per guarded act; the first is `approvalRequestId`. */
|
|
295
|
+
approvalRequestIds?: string[];
|
|
292
296
|
};
|
|
293
297
|
/** Playbook lifecycle marker in the interaction timeline (system-authored). */
|
|
294
298
|
export type PlaybookLifecyclePayload = {
|
|
@@ -38,6 +38,10 @@ export declare const AI_CAPABILITIES: readonly [{
|
|
|
38
38
|
readonly id: "embedding";
|
|
39
39
|
readonly defaultFlag: "isDefaultEmbedding";
|
|
40
40
|
readonly label: "Embeddings";
|
|
41
|
+
}, {
|
|
42
|
+
readonly id: "decision";
|
|
43
|
+
readonly defaultFlag: "isDefaultDecision";
|
|
44
|
+
readonly label: "Beslissingen";
|
|
41
45
|
}];
|
|
42
46
|
/** What an account can be the default for, and what a vendor can claim. One vocabulary. */
|
|
43
47
|
export type AiCapability = (typeof AI_CAPABILITIES)[number]["id"];
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* What one **model** can take in and give back.
|
|
3
|
+
*
|
|
4
|
+
* The question used to be asked per *vendor* (`aiVendorAcceptsAttachment`), and that is wrong in
|
|
5
|
+
* both directions: within one vendor models differ — a small fast model and its flagship do not
|
|
6
|
+
* read the same things — and it covered input only, so `tts` stayed an account flag with a form
|
|
7
|
+
* and no adapter behind it.
|
|
8
|
+
*
|
|
9
|
+
* The vocabulary is the source's, not ours (`architecture.input_modalities` on OpenRouter), and
|
|
10
|
+
* deliberately so: it is synced hourly and a translation table here would be a second place to
|
|
11
|
+
* maintain the same fact. That is why these are plain strings.
|
|
12
|
+
*/
|
|
13
|
+
export interface ModelModality {
|
|
14
|
+
in: string[];
|
|
15
|
+
out: string[];
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Does this model accept this kind — or is it simply not in the catalog?
|
|
19
|
+
*
|
|
20
|
+
* **Three states, on purpose.** `undefined` means "the catalog does not know this model", which a
|
|
21
|
+
* caller must be able to tell from a "no": the answer then falls back to what the vendor
|
|
22
|
+
* declares, rather than silently refusing every attachment on a self-hosted or brand-new model.
|
|
23
|
+
*/
|
|
24
|
+
export declare function modelAcceptsAttachment(modality: ModelModality | undefined, kind: string): boolean | undefined;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Which model answers this turn — decided per request instead of once per profile.
|
|
3
|
+
*
|
|
4
|
+
* Today nobody chooses: the model stands on the profile, or it is the account's default. Every
|
|
5
|
+
* question gets the same brain, whether it is *"mark this conversation resolved"* or *"write the
|
|
6
|
+
* quote letter"*. That is slow on the cheap questions and expensive on all of them.
|
|
7
|
+
*
|
|
8
|
+
* The routing rules are **rules**, not a model: the shape of a request is known without asking
|
|
9
|
+
* anyone. A hop to a classifier here would cost ~100 ms and a call to decide something that is
|
|
10
|
+
* free to decide.
|
|
11
|
+
*/
|
|
12
|
+
export declare const MODEL_TIERS: readonly ["fast", "standard", "deep"];
|
|
13
|
+
export type ModelTier = (typeof MODEL_TIERS)[number];
|
|
14
|
+
/** Which account and model answer at one level. Both, because a tier can live on another vendor. */
|
|
15
|
+
export interface TierBinding {
|
|
16
|
+
accountId: string;
|
|
17
|
+
model: string;
|
|
18
|
+
}
|
|
19
|
+
export type ModelTiers = Partial<Record<ModelTier, TierBinding>>;
|
|
20
|
+
/**
|
|
21
|
+
* What this turn looks like, in the things that are knowable **before** the model is asked.
|
|
22
|
+
*
|
|
23
|
+
* Deliberately no context size, though the plan names it. The size is only known after the turn
|
|
24
|
+
* is assembled, and the assembly needs the provider, which needs the account, which is what this
|
|
25
|
+
* decides. The rule it would serve (a small classify goes to `fast`) is carried by
|
|
26
|
+
* {@link RoutingRequest.expectsSchema}, and that is the case it was written for.
|
|
27
|
+
*/
|
|
28
|
+
export interface RoutingRequest {
|
|
29
|
+
/** `ask` reads, `draft` prepares, `agent` may act. Absent counts as `agent`. */
|
|
30
|
+
mode?: string;
|
|
31
|
+
/** How many tools the profile puts on the table. Zero is the cheap case. */
|
|
32
|
+
toolCount?: number;
|
|
33
|
+
hasAttachments?: boolean;
|
|
34
|
+
/** A structured answer against a schema: classify, extract, judge. */
|
|
35
|
+
expectsSchema?: boolean;
|
|
36
|
+
/** This turn repairs a failed one. Measured, not guessed — see the control-repair trace. */
|
|
37
|
+
repair?: boolean;
|
|
38
|
+
/** The ceiling the role allows. A cap, never a per-turn choice. */
|
|
39
|
+
maxTier?: ModelTier;
|
|
40
|
+
/** `soft` = a budget passed its warning threshold. `hard` never reaches here; it refuses. */
|
|
41
|
+
budgetPressure?: "none" | "soft";
|
|
42
|
+
}
|
|
43
|
+
export interface RoutingDecision {
|
|
44
|
+
tier: ModelTier;
|
|
45
|
+
/** Why, in the order the rules fired. Lands in the run so a bill can be read back. */
|
|
46
|
+
reasons: string[];
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* The tier for one request.
|
|
50
|
+
*
|
|
51
|
+
* Order matters and is the whole design: pick a base from the shape, escalate a repair, then
|
|
52
|
+
* **degrade under budget pressure rather than refuse**, and finally clamp to whatever ceiling the
|
|
53
|
+
* role set. Degrading last-but-one is deliberate — a cheaper answer is nearly always better than
|
|
54
|
+
* no answer, and today a full budget is a closed door.
|
|
55
|
+
*/
|
|
56
|
+
export declare function resolveTier(request: RoutingRequest): RoutingDecision;
|
|
57
|
+
export interface Route extends RoutingDecision {
|
|
58
|
+
accountId: string;
|
|
59
|
+
model: string;
|
|
60
|
+
/** False when no tier was bound and the profile's own choice stands. */
|
|
61
|
+
routed: boolean;
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* The tier, resolved to an account and a model.
|
|
65
|
+
*
|
|
66
|
+
* **An unconfigured organisation must change nothing.** Tiers are opt-in, so an empty map means
|
|
67
|
+
* the profile's own account and model answer exactly as they did — which is also why the walk
|
|
68
|
+
* downward exists: a half-filled ladder (`fast` and `deep`, nothing between) should still route
|
|
69
|
+
* rather than fall back to the profile and quietly ignore what was configured.
|
|
70
|
+
*/
|
|
71
|
+
export declare function resolveRoute(tiers: ModelTiers | undefined, request: RoutingRequest, fallback: TierBinding): Route;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The System One contract: ask a model a **question about a state**, get a number back.
|
|
3
|
+
*
|
|
4
|
+
* A different shape of model from every other one in this platform. A chat model is handed a
|
|
5
|
+
* prompt and writes text; this one is handed a state plus a set of typed questions and answers
|
|
6
|
+
* each of them as a probability distribution. It generates nothing — the docs say so outright —
|
|
7
|
+
* which is exactly why it is worth having: *"does this answer the question?"* is a yes/no with a
|
|
8
|
+
* degree, and a chat model asked that writes a paragraph about it.
|
|
9
|
+
*
|
|
10
|
+
* **Questions come as a set, and that is the contract rather than an optimisation.** Measured by
|
|
11
|
+
* the vendor: thirteen questions in one call against a 54k-character document were 12.2× cheaper
|
|
12
|
+
* and 10× faster than thirteen calls, and each question is scored on its own — batching adds no
|
|
13
|
+
* cross-talk. Anything that asks one question at a time here is paying twelve times over.
|
|
14
|
+
*/
|
|
15
|
+
/** What a `true` and a `false` mean for this question. Optional; the instructions usually say it. */
|
|
16
|
+
export interface NoulCriteria {
|
|
17
|
+
true?: string;
|
|
18
|
+
false?: string;
|
|
19
|
+
}
|
|
20
|
+
export interface NoulQuestion {
|
|
21
|
+
type: "noul";
|
|
22
|
+
instructions: string;
|
|
23
|
+
criteria?: NoulCriteria;
|
|
24
|
+
}
|
|
25
|
+
/** Options as `{ name: description }`. A `null` description means "the name says it". */
|
|
26
|
+
export interface ChoiceQuestion {
|
|
27
|
+
type: "choice";
|
|
28
|
+
instructions: string;
|
|
29
|
+
criteria: Record<string, string | null>;
|
|
30
|
+
}
|
|
31
|
+
/** An ordered rubric, lowest first. */
|
|
32
|
+
export interface ScoreQuestion {
|
|
33
|
+
type: "score";
|
|
34
|
+
instructions: string;
|
|
35
|
+
criteria: string[];
|
|
36
|
+
}
|
|
37
|
+
export type SystemOneQuestion = NoulQuestion | ChoiceQuestion | ScoreQuestion;
|
|
38
|
+
/**
|
|
39
|
+
* P(yes), 0..1 — the answer and the certainty in one number.
|
|
40
|
+
*
|
|
41
|
+
* **No separate `confidence`, and that is deliberate on the vendor's side**: a yes/no has two
|
|
42
|
+
* outcomes, so one number describes the whole distribution. 0.5 is not "unsure about yes", it is
|
|
43
|
+
* "yes and no weighed the same".
|
|
44
|
+
*/
|
|
45
|
+
export interface NoulAnswer {
|
|
46
|
+
type: "noul";
|
|
47
|
+
noul: number;
|
|
48
|
+
}
|
|
49
|
+
export interface ChoiceAnswer {
|
|
50
|
+
type: "choice";
|
|
51
|
+
choice: string;
|
|
52
|
+
confidence: number;
|
|
53
|
+
probabilities: Record<string, number>;
|
|
54
|
+
}
|
|
55
|
+
export interface ScoreAnswer {
|
|
56
|
+
type: "score";
|
|
57
|
+
score: number;
|
|
58
|
+
confidence: number;
|
|
59
|
+
legend?: Record<string, string>;
|
|
60
|
+
probabilities?: Record<string, number>;
|
|
61
|
+
}
|
|
62
|
+
export type SystemOneAnswer = NoulAnswer | ChoiceAnswer | ScoreAnswer;
|
|
63
|
+
export interface SystemOneRequest {
|
|
64
|
+
/** The thing being judged. A string, or a labelled object — the labels are read. */
|
|
65
|
+
state: unknown;
|
|
66
|
+
model?: string;
|
|
67
|
+
questions: Record<string, SystemOneQuestion>;
|
|
68
|
+
}
|
|
69
|
+
export interface SystemOneResponse {
|
|
70
|
+
model: string;
|
|
71
|
+
answers: Record<string, SystemOneAnswer>;
|
|
72
|
+
usage?: {
|
|
73
|
+
input_tokens?: number;
|
|
74
|
+
output_tokens?: number;
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
/** Vendor limits, so a request is refused here rather than as a 422 nobody reads. */
|
|
78
|
+
export declare const MAX_CHOICE_OPTIONS = 255;
|
|
79
|
+
export declare const MIN_SCORE_LEVELS = 2;
|
|
80
|
+
export declare const MAX_SCORE_LEVELS = 10;
|
|
81
|
+
/**
|
|
82
|
+
* What is wrong with this question set, or `null`.
|
|
83
|
+
*
|
|
84
|
+
* Checked before the call because the alternative is a 422 with a vendor's wording in a log, and
|
|
85
|
+
* because these are the mistakes a caller makes while writing a question rather than at runtime.
|
|
86
|
+
*/
|
|
87
|
+
export declare function validateQuestions(questions: Record<string, SystemOneQuestion>): string | null;
|
|
88
|
+
export type DecisionBand = "act" | "check" | "refuse";
|
|
89
|
+
/**
|
|
90
|
+
* What to *do* with a number, in three bands.
|
|
91
|
+
*
|
|
92
|
+
* The vendor is explicit that the thresholds are domain-specific and that you should start
|
|
93
|
+
* conservatively — so they are parameters, not constants, and the default is deliberately timid.
|
|
94
|
+
* The three bands are the vendor's own architecture advice: act, act-but-confirm, do not act.
|
|
95
|
+
*
|
|
96
|
+
* **`check` is the honest middle and must not be collapsed.** Two bands force every uncertain
|
|
97
|
+
* answer into either a silent action or a dead end; the middle is where a human gets asked, which
|
|
98
|
+
* is the entire reason a calibrated number is worth more than a label.
|
|
99
|
+
*/
|
|
100
|
+
export declare function decisionBand(value: number, act?: number, check?: number): DecisionBand;
|
|
101
|
+
/**
|
|
102
|
+
* Questions this model should not be asked.
|
|
103
|
+
*
|
|
104
|
+
* Straight from the vendor's own jaggedness page, and worth having in code rather than in a
|
|
105
|
+
* doc: counting, arithmetic, raw numeric values and **dates** are named failure modes — it
|
|
106
|
+
* "reads dates as text, not as ordered quantities". Every one of those has a deterministic
|
|
107
|
+
* answer in this codebase already, so asking a model is both worse and more expensive.
|
|
108
|
+
*/
|
|
109
|
+
export declare const SYSTEM_ONE_NON_GOALS: readonly ["counting or arithmetic", "comparing dates or durations", "judging whether two numbers are close", "generating text"];
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
|
|
1
|
+
import { AiDefaultFlags } from './capabilities';
|
|
2
|
+
interface AiAccountFields {
|
|
2
3
|
id: string;
|
|
3
4
|
organizationId: string;
|
|
4
5
|
vendor: string;
|
|
@@ -9,8 +10,14 @@ export interface AiAccount {
|
|
|
9
10
|
* is ignored.
|
|
10
11
|
*/
|
|
11
12
|
baseUrl?: string;
|
|
12
|
-
isDefaultLlm?: boolean;
|
|
13
|
-
isDefaultStt?: boolean;
|
|
14
|
-
isDefaultTts?: boolean;
|
|
15
|
-
isDefaultEmbedding?: boolean;
|
|
16
13
|
}
|
|
14
|
+
/**
|
|
15
|
+
* The default flags, **derived** from the capability catalog rather than listed again.
|
|
16
|
+
*
|
|
17
|
+
* They were written out by hand here, which is the drift the catalog exists to stop: adding
|
|
18
|
+
* `decision` gave every other place a fifth checkbox and this interface a fourth, so the form
|
|
19
|
+
* offered a flag the type did not have. One list, everything else follows.
|
|
20
|
+
*/
|
|
21
|
+
export interface AiAccount extends AiAccountFields, Partial<AiDefaultFlags> {
|
|
22
|
+
}
|
|
23
|
+
export {};
|
|
@@ -1,14 +1,9 @@
|
|
|
1
1
|
import { AiCapability } from './capabilities';
|
|
2
|
-
/**
|
|
3
|
-
* What a vendor can do. Was its own union of three; it is the same vocabulary the account
|
|
4
|
-
* defaults and the usage rows use, so it now names {@link AiCapability} instead of repeating it.
|
|
5
|
-
*/
|
|
6
|
-
export type AiVendorCapability = AiCapability;
|
|
7
2
|
export interface AiVendor {
|
|
8
3
|
id: string;
|
|
9
4
|
label: string;
|
|
10
5
|
/** What this vendor can do. Decides where it appears as an option in the UI. */
|
|
11
|
-
capabilities: readonly
|
|
6
|
+
capabilities: readonly AiCapability[];
|
|
12
7
|
/** Model a call runs on when it carries none itself. Absent for vendors without a chat adapter. */
|
|
13
8
|
defaultModel?: string;
|
|
14
9
|
/**
|
|
@@ -38,7 +33,7 @@ export declare function aiVendorLabel(vendorId: string): string;
|
|
|
38
33
|
*/
|
|
39
34
|
export declare function aiVendorDefaultModel(vendorId: string, fallback?: string): string;
|
|
40
35
|
/** Vendors offering this capability — for capability-filtered pickers. */
|
|
41
|
-
export declare function aiVendorsWith(capability:
|
|
36
|
+
export declare function aiVendorsWith(capability: AiCapability): AiVendor[];
|
|
42
37
|
/** True when the user has to fill in a host themselves (the vendor has no fixed one). */
|
|
43
38
|
export declare function aiVendorNeedsBaseUrl(vendorId: string): boolean;
|
|
44
39
|
/** Does this vendor accept this attachment kind? Text always works — that is prompt. */
|
|
@@ -39,6 +39,8 @@ export interface AIMessageOutput {
|
|
|
39
39
|
* question and then having the same thread draft a reply is the normal flow.
|
|
40
40
|
*/
|
|
41
41
|
export type AssistantMode = "ask" | "draft" | "agent";
|
|
42
|
+
/** Is this one of the three modes? For request bodies, which may carry anything. */
|
|
43
|
+
export declare function isAssistantMode(value: unknown): value is AssistantMode;
|
|
42
44
|
export interface AIMessage {
|
|
43
45
|
id: string;
|
|
44
46
|
organizationId: string;
|
|
@@ -97,6 +99,13 @@ export interface AIMessage {
|
|
|
97
99
|
toolsTotal?: number;
|
|
98
100
|
/** Which mode this turn ran as. Absent = `"agent"`: every row written before modes existed. */
|
|
99
101
|
mode?: AssistantMode;
|
|
102
|
+
/**
|
|
103
|
+
* Why this model answered — one line per routing rule that fired.
|
|
104
|
+
*
|
|
105
|
+
* Absent when nothing was routed, which is every organisation that bound no tiers. A reason for
|
|
106
|
+
* a choice nobody made would read as a decision.
|
|
107
|
+
*/
|
|
108
|
+
routing?: string[];
|
|
100
109
|
}
|
|
101
110
|
export type AIMessageFeedback = "up" | "down";
|
|
102
111
|
/**
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { ModelTiers } from '../ai-account/routing';
|
|
1
2
|
/**
|
|
2
3
|
* What an organisation configures for the AI app as a whole — not per profile, not per user.
|
|
3
4
|
*
|
|
@@ -5,6 +6,14 @@
|
|
|
5
6
|
* setting nobody can find.
|
|
6
7
|
*/
|
|
7
8
|
export interface AiOrgSettings {
|
|
9
|
+
/**
|
|
10
|
+
* Which account and model answer at `fast`, `standard` and `deep`.
|
|
11
|
+
*
|
|
12
|
+
* **Opt-in and empty by default.** An organisation that configures nothing keeps exactly what
|
|
13
|
+
* it has: the profile's own model answers every turn. Only once a tier is bound does the router
|
|
14
|
+
* (`resolveRoute`) get anything to choose between.
|
|
15
|
+
*/
|
|
16
|
+
tiers?: ModelTiers;
|
|
8
17
|
/**
|
|
9
18
|
* May the AI app listen in on conversations (speech to text)?
|
|
10
19
|
*
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Give one verdict on every request of a proposal that this person may decide.
|
|
3
|
+
*
|
|
4
|
+
* A park asks one request per guarded act, each with its own approvers; the person pressing the
|
|
5
|
+
* button signs the ones that are theirs. A request that is not theirs (403) or already settled
|
|
6
|
+
* (400) is counted as `waiting` rather than failing the rest — anything else is a real error.
|
|
7
|
+
*/
|
|
8
|
+
export declare function decideEach(requestIds: readonly string[], decideOne: (requestId: string) => Promise<unknown>): Promise<{
|
|
9
|
+
decided: number;
|
|
10
|
+
waiting: number;
|
|
11
|
+
}>;
|
|
12
|
+
/** The requests a proposal waits on, from the list or the single id older rows carry. */
|
|
13
|
+
export declare function proposalRequestIds(source: {
|
|
14
|
+
approvalRequestIds?: readonly string[] | null;
|
|
15
|
+
approvalRequestId?: string | null;
|
|
16
|
+
}): string[];
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { EvalCase, EvalCaseResult, EvalRun } from './types';
|
|
2
|
+
/**
|
|
3
|
+
* Run a suite. The judge is injected, which is the whole reason this is one function.
|
|
4
|
+
*
|
|
5
|
+
* A deterministic suite (rule correctness, a classification against a known answer) judges by
|
|
6
|
+
* comparison and costs nothing; a judged suite asks a model. Both are the same loop, and keeping
|
|
7
|
+
* that loop in one place is what makes "put this in CI" a decision about the judge rather than a
|
|
8
|
+
* second implementation.
|
|
9
|
+
*
|
|
10
|
+
* **Sequential, not parallel.** A judged suite is a batch of model calls, and firing forty at
|
|
11
|
+
* once is how a rate limit turns a measurement into a red build. A deterministic judge is fast
|
|
12
|
+
* enough that the difference does not register.
|
|
13
|
+
*/
|
|
14
|
+
export declare function runEvalSuite<I, E>(suite: string, target: string, cases: readonly EvalCase<I, E>[], judge: (testCase: EvalCase<I, E>) => Promise<EvalCaseResult> | EvalCaseResult, now?: number): Promise<EvalRun>;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Measuring whether this platform is any good at the things it decides.
|
|
3
|
+
*
|
|
4
|
+
* Today that exists once, as a test file inside the playbook folder: a handful of cases, opt-in,
|
|
5
|
+
* never in CI. That is not small-but-good, it is the wrong shape — it measures one kind of thing
|
|
6
|
+
* (a playbook prompt) while seven things need exactly the same machinery: prompt quality, the
|
|
7
|
+
* accuracy of a classification, **rule correctness**, retrieval, tool selection, whether an agent
|
|
8
|
+
* turn reached its control move, and live answer quality.
|
|
9
|
+
*
|
|
10
|
+
* Two design rules live in the shape itself. A case carries either an `expected` value **or** a
|
|
11
|
+
* `rubric`, because those are the two ways anything here can be judged: exactly, or by opinion —
|
|
12
|
+
* and an opinion needs a judge, which costs money and has to be calibrated. And a case is
|
|
13
|
+
* something you can *harvest*: every correction in the feedback loop is by definition a labelled
|
|
14
|
+
* case, while a golden set written by hand is out of date within a quarter.
|
|
15
|
+
*/
|
|
16
|
+
export interface EvalCase<Input = unknown, Expected = unknown> {
|
|
17
|
+
id: string;
|
|
18
|
+
/** What kind of thing is judged: `rule`, `classify`, `retrieval`, `tool-selection`, … */
|
|
19
|
+
subject: string;
|
|
20
|
+
input: Input;
|
|
21
|
+
/** The exact answer, when there is one. Mutually exclusive with {@link EvalCase.rubric}. */
|
|
22
|
+
expected?: Expected;
|
|
23
|
+
/** What "good" means, for a case only a judge can settle. */
|
|
24
|
+
rubric?: string;
|
|
25
|
+
tags?: string[];
|
|
26
|
+
}
|
|
27
|
+
export interface EvalCaseResult {
|
|
28
|
+
caseId: string;
|
|
29
|
+
ok: boolean;
|
|
30
|
+
/** What actually happened, in the case's own terms. Present on a failure; optional otherwise. */
|
|
31
|
+
got?: unknown;
|
|
32
|
+
/** Why it failed, in a sentence a person can act on. */
|
|
33
|
+
note?: string;
|
|
34
|
+
}
|
|
35
|
+
export interface EvalRun {
|
|
36
|
+
suite: string;
|
|
37
|
+
/** What was measured: a model id, a rule set version, a prompt. */
|
|
38
|
+
target: string;
|
|
39
|
+
at: number;
|
|
40
|
+
/** Passed over total, 0..1. `1` on an empty suite — nothing failed. */
|
|
41
|
+
score: number;
|
|
42
|
+
perCase: EvalCaseResult[];
|
|
43
|
+
}
|
|
44
|
+
export declare function summariseEval(suite: string, target: string, perCase: readonly EvalCaseResult[], at?: number): EvalRun;
|
|
45
|
+
/** The failures, as lines. What a CI log should print and nothing more. */
|
|
46
|
+
export declare function failureLines(run: EvalRun): string[];
|
|
@@ -11,10 +11,10 @@ export interface Inbox {
|
|
|
11
11
|
/**
|
|
12
12
|
* Hours a first reply may take in this inbox before a conversation counts as at risk.
|
|
13
13
|
*
|
|
14
|
-
* Absent =
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
14
|
+
* **Absent = no service target at all**, not a default. A ladder per priority used to stand in
|
|
15
|
+
* (1/4/8/24) and that was a hardcoded opinion: every inbox, and every conversation outside one,
|
|
16
|
+
* silently carried a promise nobody made. Deliberately not being in an SLA — personal mail —
|
|
17
|
+
* is the common case, so it is the one that needs no configuration.
|
|
18
18
|
*
|
|
19
19
|
* One number and not four (one per priority): an inbox usually *is* the agreement ("support:
|
|
20
20
|
* within 4 hours"), and four fields per inbox is four things to keep consistent while the
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { AttentionCtx, ParkMark,
|
|
1
|
+
import { AttentionCtx, ParkMark, Scored } from '../../platform/attention';
|
|
2
2
|
import { Interaction } from './types';
|
|
3
3
|
/** The only statuses that can ask for attention; `closed` and `snoozed` never compete. */
|
|
4
4
|
export declare const ATTENTION_STATUSES: readonly ["open"];
|
|
@@ -15,11 +15,6 @@ export declare const ATTENTION_STATUSES: readonly ["open"];
|
|
|
15
15
|
* larger than any positive contribution, so bulk always ranks below every real conversation.
|
|
16
16
|
*/
|
|
17
17
|
export declare const BULK_PENALTY = 1000;
|
|
18
|
-
/**
|
|
19
|
-
* Hours a first response is "allowed" per priority before an open interaction counts as
|
|
20
|
-
* SLA-at-risk. Also consumed by the Mijn dag KPI and the wait heuristic.
|
|
21
|
-
*/
|
|
22
|
-
export declare const SLA_HOURS_BY_PRIORITY: Record<Priority, number>;
|
|
23
18
|
/**
|
|
24
19
|
* How long you wait for a reply before the conversation asks for attention again. Without this
|
|
25
20
|
* bound a thread the customer never replied to would stay out of sight forever.
|
|
@@ -48,16 +43,17 @@ export declare function isReminderActive(reminder: ParkMark | undefined, i: Inte
|
|
|
48
43
|
/** Whole hours an open interaction has been waiting (never negative). */
|
|
49
44
|
export declare function waitHoursOf(i: Interaction, now: number): number;
|
|
50
45
|
/**
|
|
51
|
-
* The first-reply budget for this conversation
|
|
46
|
+
* The first-reply budget for this conversation in hours, or `undefined` when nobody agreed one.
|
|
52
47
|
*
|
|
53
|
-
*
|
|
54
|
-
*
|
|
55
|
-
*
|
|
48
|
+
* **There is deliberately no fallback.** A ladder per priority used to fill this in (1/4/8/24),
|
|
49
|
+
* which invented an agreement nobody made: a personal mailbox got an eight-hour promise for the
|
|
50
|
+
* sole reason that its priority was unset. "No service target" is a valid state — most mail is
|
|
51
|
+
* in it — and it has to look different from a target someone actually configured.
|
|
56
52
|
*
|
|
57
53
|
* A non-positive stored value is treated as absent: "0 hours" is not an agreement, it is an
|
|
58
54
|
* empty field that made it through a form.
|
|
59
55
|
*/
|
|
60
|
-
export declare function slaHoursFor(i: Interaction, slaByInbox?: Record<string, number>): number;
|
|
56
|
+
export declare function slaHoursFor(i: Interaction, slaByInbox?: Record<string, number>): number | undefined;
|
|
61
57
|
/**
|
|
62
58
|
* Index inboxes the way {@link slaHoursFor} wants them: only the ones that named a norm.
|
|
63
59
|
*
|
|
@@ -71,8 +67,11 @@ export declare function slaHoursByInbox(inboxes: readonly {
|
|
|
71
67
|
/**
|
|
72
68
|
* True when an open interaction has waited past its first-reply budget.
|
|
73
69
|
*
|
|
74
|
-
*
|
|
75
|
-
*
|
|
70
|
+
* No budget, no risk. A conversation in an inbox that named no norm cannot be late, because
|
|
71
|
+
* there is nothing to be late for — so the answer is `false`, not "unknown". The one reader is a
|
|
72
|
+
* badge, and a badge that fires on an agreement that does not exist is the bug this removed.
|
|
73
|
+
*
|
|
74
|
+
* `slaByInbox` maps inbox id -> agreed hours; leave it out and nothing is ever at risk.
|
|
76
75
|
*/
|
|
77
76
|
export declare function isSlaAtRisk(i: Interaction, now: number, slaByInbox?: Record<string, number>): boolean;
|
|
78
77
|
/**
|
|
@@ -1,20 +1,9 @@
|
|
|
1
1
|
import { ActingIdentity } from '../../platform/identity';
|
|
2
|
-
import { toActingIdentity } from '../scope/actor';
|
|
3
2
|
import { OwnerScope } from '../scope/types';
|
|
4
|
-
/**
|
|
5
|
-
* `ownerScope` **is** a playbook's actor: there is no separate `runAs` field. Visibility, working
|
|
6
|
-
* area and attribution therefore all follow from the same field.
|
|
7
|
-
*
|
|
8
|
-
* On the storage side the personal variant is called `personal` (as with every other entity), on
|
|
9
|
-
* the execution side `user` (as with every other acting identity). This function is that
|
|
10
|
-
* translation, and the only place it belongs.
|
|
11
|
-
*/
|
|
12
|
-
export declare const playbookActor: typeof toActingIdentity;
|
|
13
3
|
/**
|
|
14
4
|
* Who this run acts for.
|
|
15
5
|
*
|
|
16
|
-
* **Two different questions
|
|
17
|
-
* {@link playbookActor}:**
|
|
6
|
+
* **Two different questions**, which is why this is not just `toActingIdentity(ownerScope)`:
|
|
18
7
|
*
|
|
19
8
|
* - *who acts* — the agent, when one is designated;
|
|
20
9
|
* - *who may manage this* — `ownerScope`, and that stays with humans.
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A parked run's address in the approval layer: `playbook-run:<runId>~<step>`.
|
|
3
|
+
*
|
|
4
|
+
* The park is part of the key on purpose. The approval layer de-duplicates on `(scopeKey, act)`
|
|
5
|
+
* and keeps one approved row per key, so a flow that parks, resumes and parks again on a later
|
|
6
|
+
* step needs a key per park — while a retry of the *same* park must find its open request.
|
|
7
|
+
* `~` because the key travels in paths too, where `/` and `.` already mean something.
|
|
8
|
+
*/
|
|
9
|
+
export declare const PLAYBOOK_RUN_SCOPE_KIND = "playbook-run";
|
|
10
|
+
/** The one act a parked run asks for: "carry out what was proposed". */
|
|
11
|
+
export declare const PLAYBOOK_RUN_APPROVAL_ACT = "run";
|
|
12
|
+
export declare function runApprovalKey(runId: string, step?: number): string;
|
|
13
|
+
/** The run behind an approval key's ref (`<runId>~<step>`), or the ref itself without a step. */
|
|
14
|
+
export declare function runIdOfApprovalRef(ref: string): string;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { Autonomy } from './types';
|
|
2
|
+
/** Is this one of the three settings? For request bodies, which may carry anything. */
|
|
3
|
+
export declare function isAutonomy(value: unknown): value is Autonomy;
|
|
4
|
+
/**
|
|
5
|
+
* How much a run may do on its own, decided **once** at its start and frozen on the run. The
|
|
6
|
+
* playbook's setting wins where there is one (that is where the organisation configured it), then
|
|
7
|
+
* the agent's, then `suggest`.
|
|
8
|
+
*
|
|
9
|
+
* `demoted`: several autonomous playbooks would act outward on the same subject at once. They
|
|
10
|
+
* keep doing internal work themselves; a human decides which one goes out.
|
|
11
|
+
*/
|
|
12
|
+
export declare function resolveAutonomy(input: {
|
|
13
|
+
playbook?: {
|
|
14
|
+
autonomy?: Autonomy;
|
|
15
|
+
} | null;
|
|
16
|
+
agent?: {
|
|
17
|
+
autonomy?: Autonomy;
|
|
18
|
+
} | null;
|
|
19
|
+
demoted?: boolean;
|
|
20
|
+
}): Autonomy;
|
|
21
|
+
/**
|
|
22
|
+
* A step's own setting may make it more careful than its run, never less: the run's value is
|
|
23
|
+
* where a demotion and the agent's mandate already landed.
|
|
24
|
+
*/
|
|
25
|
+
export declare function narrowAutonomy(run: Autonomy | undefined, step: Autonomy | undefined): Autonomy;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Readable name of an action/tool for a human: `
|
|
2
|
+
* Readable name of an action/tool for a human: `communication__update_interaction` becomes
|
|
3
3
|
* `communication · update interaction`.
|
|
4
4
|
*
|
|
5
5
|
* Shared, because the proposal card in comms already had this helper while the run timeline in the
|
|
@@ -7,8 +7,6 @@
|
|
|
7
7
|
* `voorstel: tool:mcp__jira__create_issue`.
|
|
8
8
|
*/
|
|
9
9
|
export declare function humanizeAction(action: string): string;
|
|
10
|
-
/** The namespaced tool id without the step's `tool:` prefix. */
|
|
11
|
-
export declare function toolName(action: string): string;
|
|
12
10
|
/**
|
|
13
11
|
* The tool's own name, without the namespace that says who owns it:
|
|
14
12
|
* `work__create_work_item` and `mcp__jira__create_issue` become `create_work_item` and
|