@xema/omni-protocol 0.1.92 → 0.1.93
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/index.d.ts +4 -4
- package/dist/testing.js +1 -1
- package/dist/validation.d.ts +1 -1
- package/dist/validation.js +10 -10
- package/guide.md +22 -16
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -236,7 +236,7 @@ export interface AuthenticationFailure {
|
|
|
236
236
|
/** What a lead may do with their team. Declared by presence. */
|
|
237
237
|
export interface TeamCapabilities {
|
|
238
238
|
/**
|
|
239
|
-
* This lead may act on their team's breaks through `executeTeamBreak` --
|
|
239
|
+
* This lead may act on their team's breaks through `executeTeamBreak` -- force-break, end-forced-break, decide-break-request,
|
|
240
240
|
* set policy -- as far as the provider supports; a command it lacks answers
|
|
241
241
|
* `omni.capability-not-enabled`. Requires `executeTeamBreak`.
|
|
242
242
|
*/
|
|
@@ -1390,7 +1390,7 @@ export interface TeamListenCommandRequest {
|
|
|
1390
1390
|
command: TeamListenCommand;
|
|
1391
1391
|
}
|
|
1392
1392
|
export type TeamBreakCommand = {
|
|
1393
|
-
type: "decide";
|
|
1393
|
+
type: "decide-break-request";
|
|
1394
1394
|
memberId: UserId;
|
|
1395
1395
|
decision: "granted" | "denied";
|
|
1396
1396
|
reason?: string;
|
|
@@ -1400,12 +1400,12 @@ export type TeamBreakCommand = {
|
|
|
1400
1400
|
}
|
|
1401
1401
|
/** `reasonId` names a published `BreakReason.id`, required whenever the provider publishes reasons: the member's forced break carries it as `activeReasonId`. */
|
|
1402
1402
|
| {
|
|
1403
|
-
type: "force";
|
|
1403
|
+
type: "force-break";
|
|
1404
1404
|
memberId: UserId;
|
|
1405
1405
|
reasonId?: string;
|
|
1406
1406
|
reason?: string;
|
|
1407
1407
|
} | {
|
|
1408
|
-
type: "
|
|
1408
|
+
type: "end-forced-break";
|
|
1409
1409
|
memberId: UserId;
|
|
1410
1410
|
};
|
|
1411
1411
|
export type TeamCommandResult = {
|
package/dist/testing.js
CHANGED
|
@@ -1867,7 +1867,7 @@ async function driveOneCall(drive) {
|
|
|
1867
1867
|
}
|
|
1868
1868
|
// A request goes not-requested -> awaiting-decision | granted; a commit goes granted ->
|
|
1869
1869
|
// starting-after-task | in-effect; work ending goes starting-after-task -> in-effect; a denial,
|
|
1870
|
-
// a cancel, an end or a
|
|
1870
|
+
// a cancel, an agent end or a lead end goes back to not-requested; a forced break arrives in-effect
|
|
1871
1871
|
// with `forced`. Nothing else is a move the guide describes.
|
|
1872
1872
|
/** What a stream has said about the agent's break, and the moves it may not make. */
|
|
1873
1873
|
export class BreakStream {
|
package/dist/validation.d.ts
CHANGED
|
@@ -51,7 +51,7 @@ export declare function validateTask(task: unknown, context: TaskValidationConte
|
|
|
51
51
|
export declare function validateBreakStatus(state: unknown, tasks: unknown, path?: string): ProtocolViolation[];
|
|
52
52
|
/**
|
|
53
53
|
* Checks lead break dispatch using current authentication, transport, team member list (`team`) and,
|
|
54
|
-
* for force/
|
|
54
|
+
* for force-break/end-forced-break, the target's full `memberBreak`. The provider must authorize the target
|
|
55
55
|
* and recheck the decision atomically; a team member list is not authority to act after it has changed.
|
|
56
56
|
*/
|
|
57
57
|
export declare function validateTeamBreakCommand(request: unknown, context: unknown, path?: string): ProtocolViolation[];
|
package/dist/validation.js
CHANGED
|
@@ -1350,7 +1350,7 @@ export function validateBreakStatus(state, tasks, path = "snapshot") {
|
|
|
1350
1350
|
}
|
|
1351
1351
|
/**
|
|
1352
1352
|
* Checks lead break dispatch using current authentication, transport, team member list (`team`) and,
|
|
1353
|
-
* for force/
|
|
1353
|
+
* for force-break/end-forced-break, the target's full `memberBreak`. The provider must authorize the target
|
|
1354
1354
|
* and recheck the decision atomically; a team member list is not authority to act after it has changed.
|
|
1355
1355
|
*/
|
|
1356
1356
|
export function validateTeamBreakCommand(request, context, path = "teamBreakCommand") {
|
|
@@ -1366,8 +1366,8 @@ export function validateTeamBreakCommand(request, context, path = "teamBreakComm
|
|
|
1366
1366
|
into.require(isPlainObject(caps.team) && caps.team.breakControl === true, "team.break.command.capability", path, "the login must declare team.breakControl");
|
|
1367
1367
|
const command = request.command;
|
|
1368
1368
|
const allowed = {
|
|
1369
|
-
decide: ["type", "memberId", "decision", "reason"], policy: ["type", "policy"],
|
|
1370
|
-
force: ["type", "memberId", "reasonId", "reason"],
|
|
1369
|
+
"decide-break-request": ["type", "memberId", "decision", "reason"], policy: ["type", "policy"],
|
|
1370
|
+
"force-break": ["type", "memberId", "reasonId", "reason"], "end-forced-break": ["type", "memberId"],
|
|
1371
1371
|
};
|
|
1372
1372
|
const fields = typeof command.type === "string" && Object.hasOwn(allowed, command.type) ? allowed[command.type] : undefined;
|
|
1373
1373
|
if (!fields) {
|
|
@@ -1390,20 +1390,20 @@ export function validateTeamBreakCommand(request, context, path = "teamBreakComm
|
|
|
1390
1390
|
const member = members.find((m) => isPlainObject(m) && m.id === command.memberId);
|
|
1391
1391
|
const self = isPlainObject(auth.identity) ? auth.identity.id : undefined;
|
|
1392
1392
|
into.require(isPlainObject(member) && command.memberId !== self, "team.break.command.member", path, "the target must be another member of the current authorized team member list");
|
|
1393
|
-
if (command.type === "decide") {
|
|
1393
|
+
if (command.type === "decide-break-request") {
|
|
1394
1394
|
into.require(command.decision === "granted" || command.decision === "denied", "team.break.command.decision", path, "decide granted or denied");
|
|
1395
1395
|
into.require(isPlainObject(member) && member.break === "awaiting-decision", "team.break.command.awaiting", path, "decide only a currently awaiting-decision request");
|
|
1396
1396
|
}
|
|
1397
1397
|
else {
|
|
1398
1398
|
validateBreakState(context.memberBreak, `${path}.memberBreak`, into);
|
|
1399
1399
|
const state = isPlainObject(context.memberBreak) ? context.memberBreak : {};
|
|
1400
|
-
if (command.type === "
|
|
1401
|
-
into.require(state.forced !== undefined && (state.approval === "in-effect" || state.approval === "starting-after-task"), "team.break.command.
|
|
1402
|
-
if (command.type === "force") {
|
|
1400
|
+
if (command.type === "end-forced-break")
|
|
1401
|
+
into.require(state.forced !== undefined && (state.approval === "in-effect" || state.approval === "starting-after-task"), "team.break.command.endForcedBreak", path, "end a currently forced break");
|
|
1402
|
+
if (command.type === "force-break") {
|
|
1403
1403
|
if (command.reasonId !== undefined)
|
|
1404
1404
|
into.filled(command.reasonId, "team.break.command.reasonId", path, "reasonId must not be empty");
|
|
1405
1405
|
const reasons = Array.isArray(state.reasons) ? state.reasons : [];
|
|
1406
|
-
into.require(state.reasons === undefined ? command.reasonId === undefined : reasons.some((r) => isPlainObject(r) && r.id === command.reasonId), "team.break.command.reasonId", path, "force uses the target's currently published reason codes");
|
|
1406
|
+
into.require(state.reasons === undefined ? command.reasonId === undefined : reasons.some((r) => isPlainObject(r) && r.id === command.reasonId), "team.break.command.reasonId", path, "force-break uses the target's currently published reason codes");
|
|
1407
1407
|
}
|
|
1408
1408
|
}
|
|
1409
1409
|
return into.violations;
|
|
@@ -1459,7 +1459,7 @@ export function validateBreakCommand(method, request, state, context, path = "br
|
|
|
1459
1459
|
into.require(approval === "awaiting-decision" || approval === "granted", "break.command.cancel.precommit", path, "cancel only a pre-commit request; a raced commit requires commit recovery");
|
|
1460
1460
|
if (method === "endBreak") {
|
|
1461
1461
|
into.require(approval === "starting-after-task" || approval === "in-effect", "break.command.end.started", path, "end a committed break, including a returning provider still finishing work");
|
|
1462
|
-
into.require(state.forced === undefined, "break.command.end.forced", path, "an agent cannot end a forced break; an authorized lead
|
|
1462
|
+
into.require(state.forced === undefined, "break.command.end.forced", path, "an agent cannot end a forced break; an authorized lead ends it");
|
|
1463
1463
|
}
|
|
1464
1464
|
}
|
|
1465
1465
|
return into.violations;
|
|
@@ -1557,7 +1557,7 @@ function validateBreakState(value, path, into) {
|
|
|
1557
1557
|
into.require(seen.has(value.activeReasonId), "break.activeReasonId.known", `${path}.activeReasonId`, `activeReasonId names a reason the provider did not publish: ${value.activeReasonId}`);
|
|
1558
1558
|
}
|
|
1559
1559
|
// A break in effect, or about to be, on a provider that publishes reasons is on one of them: an
|
|
1560
|
-
// forced one included, since the lead's force command named it. A break of no kind is a break whose
|
|
1560
|
+
// forced one included, since the lead's force-break command named it. A break of no kind is a break whose
|
|
1561
1561
|
// rules -- who may listen through it, whether it counts -- nobody can apply.
|
|
1562
1562
|
ruleEvaluated("break.activeReasonId.required");
|
|
1563
1563
|
if (seen.size > 0 && (value.approval === "in-effect" || value.approval === "starting-after-task")) {
|
package/guide.md
CHANGED
|
@@ -1034,10 +1034,10 @@ type TeamLeadAssistCommand =
|
|
|
1034
1034
|
| { type: "decline"; requestId: string; reason?: string };
|
|
1035
1035
|
|
|
1036
1036
|
type TeamBreakCommand =
|
|
1037
|
-
| { type: "decide"; memberId: UserId; decision: "granted" | "denied"; reason?: string }
|
|
1037
|
+
| { type: "decide-break-request"; memberId: UserId; decision: "granted" | "denied"; reason?: string }
|
|
1038
1038
|
| { type: "policy"; policy: "ask" | "auto-approve" | "suspended" }
|
|
1039
|
-
| { type: "force"; memberId: UserId; reasonId?: string; reason?: string }
|
|
1040
|
-
| { type: "
|
|
1039
|
+
| { type: "force-break"; memberId: UserId; reasonId?: string; reason?: string }
|
|
1040
|
+
| { type: "end-forced-break"; memberId: UserId };
|
|
1041
1041
|
|
|
1042
1042
|
type TeamBreakCommandRequest = {
|
|
1043
1043
|
command: TeamBreakCommand;
|
|
@@ -1924,7 +1924,7 @@ them from what arrives later.
|
|
|
1924
1924
|
| --- | --- |
|
|
1925
1925
|
| `breaks` | This login may request a break. Requires the four break methods on the connection. |
|
|
1926
1926
|
| `team` | This login leads a team. The provider publishes a `TeamMembers` object to it on every snapshot — `members: []` when nobody is in it — and to nobody else. |
|
|
1927
|
-
| `team.breakControl` | This lead may act on their team's breaks through `executeTeamBreak` — force,
|
|
1927
|
+
| `team.breakControl` | This lead may act on their team's breaks through `executeTeamBreak` — force-break, end-forced-break, decide-break-request, set policy — as far as the provider supports; a command it lacks answers `omni.capability-not-enabled`. Omni asks for a decision only against a member whose `break` is `awaiting-decision`, so a provider that grants on request is never asked to decide. Requires `executeTeamBreak`. |
|
|
1928
1928
|
| `team.leadAssistControl` | This lead may join a member's call on request. Requires `executeTeamLeadAssist`. |
|
|
1929
1929
|
| `team.listeningControl` | This lead may listen to a member's call unasked, in the listed modes and no others: `listen`, `coach`, `join-call`. The list always includes `listen`, since the other two begin from one. Requires `executeTeamListen`. See **Listening to a call**. |
|
|
1930
1930
|
| `team.policyControl` | This lead sets the team's policy per capability — on, off, or the person's — within what the queue allows. Requires `executeTeamPolicy`; the team member list carries `policies`. |
|
|
@@ -3646,9 +3646,15 @@ Migration: ImposedBreak is now `ForcedBreak`, and the former imposed field is no
|
|
|
3646
3646
|
`BreakState.forced`. Diagnostic and harness coverage names use `break.forced`; the agent-end
|
|
3647
3647
|
diagnostic is `break.command.end.forced`. Update hosts and providers together. The old field
|
|
3648
3648
|
is rejected, including when both spellings are sent; no compatibility alias is provided.
|
|
3649
|
-
The team break command formerly named
|
|
3650
|
-
|
|
3651
|
-
|
|
3649
|
+
The team break command formerly named decide is now `decide-break-request`. The former
|
|
3650
|
+
spelling is rejected without an alias; the granted/denied decisions and pending-request
|
|
3651
|
+
prerequisite are unchanged.
|
|
3652
|
+
The team break command formerly named place is now `force-break`. Hosts and providers must
|
|
3653
|
+
adopt the new command together; place and the interim force spelling are rejected without aliases.
|
|
3654
|
+
The team break command formerly named release is now `end-forced-break`, and its diagnostic is
|
|
3655
|
+
`team.break.command.endForcedBreak`. The former release command and the interim end spelling are rejected without aliases.
|
|
3656
|
+
**End a forced break** is a lead action through `executeTeamBreak`; the agent’s `endBreak`
|
|
3657
|
+
method still cannot end a forced break. Break ordering and permission rules are unchanged.
|
|
3652
3658
|
|
|
3653
3659
|
`ForcedBreak` says who forced the break, whether automatic ending is enabled, and, when enabled,
|
|
3654
3660
|
when the provider will end it. A break the agent did not choose is not manually resumable by them.
|
|
@@ -3664,7 +3670,7 @@ a display name.
|
|
|
3664
3670
|
**A forced break travels with `in-effect` or `starting-after-task`, and nothing else.** It is a
|
|
3665
3671
|
break in progress or about to be; beside `granted` or `awaiting-decision` the host would read a
|
|
3666
3672
|
request the agent never made and commit it (`break.forced.approval`). Where the provider publishes
|
|
3667
|
-
`reasons`, the lead's `force` named one, and the member's state carries it as `activeReasonId`.
|
|
3673
|
+
`reasons`, the lead's `force-break` named one, and the member's state carries it as `activeReasonId`.
|
|
3668
3674
|
|
|
3669
3675
|
For example:
|
|
3670
3676
|
|
|
@@ -3770,10 +3776,10 @@ request object only to the request method, and undefined to the other three.
|
|
|
3770
3776
|
| `cancelBreak` | `awaiting-decision` or `granted`. A concurrent commit winning still answers `omni.break-already-committed` and requires recovery. |
|
|
3771
3777
|
| `endBreak` | `in-effect` or `starting-after-task` during reconciliation; an agent cannot end a forced break. |
|
|
3772
3778
|
|
|
3773
|
-
Use `validateTeamBreakCommand(request, context)` for lead decisions, forcing a break,
|
|
3779
|
+
Use `validateTeamBreakCommand(request, context)` for lead decisions, forcing a break, ending a forced break and
|
|
3774
3780
|
policy commands. It requires the live lead capability and active transport, a current target
|
|
3775
|
-
team member list for member commands, and the target's complete break state for forcing/
|
|
3776
|
-
Approve/deny requires an awaiting decision; forcing a break uses the target's reason codes;
|
|
3781
|
+
team member list for member commands, and the target's complete break state for forcing/ending a break.
|
|
3782
|
+
Approve/deny requires an awaiting decision; forcing a break uses the target's reason codes; ending a forced break
|
|
3777
3783
|
requires a forced committed break. The context's target state must belong to the named member;
|
|
3778
3784
|
that association and backend authorization are provider responsibilities.
|
|
3779
3785
|
|
|
@@ -4100,10 +4106,10 @@ One method, `executeTeamBreak`, taking a discriminated command exactly as `execu
|
|
|
4100
4106
|
|
|
4101
4107
|
| Command | Effect |
|
|
4102
4108
|
| --- | --- |
|
|
4103
|
-
| `{ type: "decide", memberId: UserId, decision, reason? }` | Settles one pending request. `decision` is `granted` or `denied`. A grant moves the member to `granted`; a denial ends the request and moves it directly to `not-requested`. |
|
|
4109
|
+
| `{ type: "decide-break-request", memberId: UserId, decision, reason? }` | Settles one pending request. `decision` is `granted` or `denied`. A grant moves the member to `granted`; a denial ends the request and moves it directly to `not-requested`. |
|
|
4104
4110
|
| `{ type: "policy", policy }` | `ask`, `auto-approve`, or `suspended`. |
|
|
4105
|
-
| `{ type: "force", memberId: UserId, reasonId?, reason? }` | Puts a member on a break they did not ask for. `reasonId` names a published `BreakReason.id` and is required whenever the provider publishes `reasons`; the member's forced break carries it as `activeReasonId`, so its kind is known. |
|
|
4106
|
-
| `{ type: "
|
|
4111
|
+
| `{ type: "force-break", memberId: UserId, reasonId?, reason? }` | Puts a member on a break they did not ask for. `reasonId` names a published `BreakReason.id` and is required whenever the provider publishes `reasons`; the member's forced break carries it as `activeReasonId`, so its kind is known. |
|
|
4112
|
+
| `{ type: "end-forced-break", memberId: UserId }` | Ends a forced break on that member, whoever forced it. |
|
|
4107
4113
|
|
|
4108
4114
|
`memberId` is this provider's own identifier for the member, as published on its team member list. It is
|
|
4109
4115
|
never an identifier from another provider, and Omni does not translate between them; names come
|
|
@@ -4111,7 +4117,7 @@ from `describeUsers()`.
|
|
|
4111
4117
|
|
|
4112
4118
|
`suspended` means requests are **rejected outright** rather than left pending — nobody is coming to
|
|
4113
4119
|
approve them. A provider that suspends breaks must also publish `mayAsk: false` to the team's
|
|
4114
|
-
agents so they see it before asking. A `force` must likewise reach that member as a `forced` break
|
|
4120
|
+
agents so they see it before asking. A `force-break` must likewise reach that member as a `forced` break
|
|
4115
4121
|
on their own `BreakState`, or they are stopped from working with no way to see why.
|
|
4116
4122
|
|
|
4117
4123
|
What happens when no lead is online — auto-approving, for instance — is the provider's decision and is
|
|
@@ -4982,7 +4988,7 @@ operation is dispatched. The source must fence delayed operations against its ow
|
|
|
4982
4988
|
Normal order is `not-requested` → `awaiting-decision` → `granted` →
|
|
4983
4989
|
`starting-after-task` → `in-effect` → `not-requested`. Auto-approval may go directly to
|
|
4984
4990
|
`granted`; a commit with no outstanding work may go directly to `in-effect`. Denial/cancel
|
|
4985
|
-
returns a precommit request to `not-requested`; an authorized end
|
|
4991
|
+
returns a precommit request to `not-requested`; an authorized agent or lead end returns a committed
|
|
4986
4992
|
break there. Same-state restatements are allowed. An evidenced forced break is the explicit
|
|
4987
4993
|
exception to requesting/granting, and must carry its forced actor/state. Neither skipped
|
|
4988
4994
|
publication nor a host-local guess creates another exception. A later normal attempt starts
|