@xema/omni-protocol 0.1.7 → 0.1.8
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/README.md +17 -2
- package/guide.md +51 -21
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -59,9 +59,11 @@ assignments, break denial and retry, wrap timeout, browser isolation.
|
|
|
59
59
|
> conforming one. A suite that only asserts "this conforming case does not throw" passes unchanged
|
|
60
60
|
> if the helper is gutted, so pair every positive case with the violating twin.
|
|
61
61
|
|
|
62
|
-
##
|
|
62
|
+
## Three things TypeScript will not catch for you
|
|
63
63
|
|
|
64
|
-
|
|
64
|
+
All found by adapters against this contract, and all produce a green build over a wrong shape.
|
|
65
|
+
Two share one cause: TypeScript checks extra keys only on a literal that is the thing directly
|
|
66
|
+
assigned. Move the literal anywhere else and the check is gone.
|
|
65
67
|
|
|
66
68
|
**Conditional spreads are the blind spot on a task literal.** A key inside
|
|
67
69
|
`...(cond ? { … } : {})` is never checked against the task type, and `satisfies Task<C>` on the
|
|
@@ -88,6 +90,19 @@ switch (command.action) {
|
|
|
88
90
|
}
|
|
89
91
|
```
|
|
90
92
|
|
|
93
|
+
**A `const` fixture escapes excess-property checking.** Park a literal in a variable and it is no
|
|
94
|
+
longer the thing directly assigned — the same reason the conditional spread escapes — so a shared
|
|
95
|
+
test fixture keeps a field the contract has dropped: `tsc` says nothing and the suite is
|
|
96
|
+
confidently green over a shape that no longer exists. Annotate the `const` or `satisfies` it where
|
|
97
|
+
it is declared; either names the field on the next build.
|
|
98
|
+
|
|
99
|
+
```ts
|
|
100
|
+
take({ reasonId, requestedAt }); // error: requestedAt
|
|
101
|
+
const request = { reasonId, requestedAt }; take(request); // no error — the hole
|
|
102
|
+
const request: BreakRequest = { reasonId, requestedAt }; // error: requestedAt
|
|
103
|
+
const request = { reasonId, requestedAt } satisfies BreakRequest; // error: requestedAt
|
|
104
|
+
```
|
|
105
|
+
|
|
91
106
|
## Building
|
|
92
107
|
|
|
93
108
|
```
|
package/guide.md
CHANGED
|
@@ -914,10 +914,17 @@ is applied.
|
|
|
914
914
|
|
|
915
915
|
### 6. An unsettled result is unknown, and unknown is not retried
|
|
916
916
|
|
|
917
|
-
A settled result is a fact. A promise that rejects with no result
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
917
|
+
A settled result is a fact. A promise that rejects with no result means *unknown*: the provider
|
|
918
|
+
may have done it or not, and neither Omni nor an adapter on the agent's PC can find out in time to
|
|
919
|
+
make a repeat safe. Omni does not retry; if the agent acts again it is a new command.
|
|
920
|
+
|
|
921
|
+
On a persistent ordered transport there is only one way a command goes unsettled: the connection
|
|
922
|
+
went away underneath it. **An adapter that cannot settle a command has lost its transport, and
|
|
923
|
+
says so** — `provider-status` `connecting`, reconnect, snapshot — whichever channel the command
|
|
924
|
+
actually travelled on. An unsettled promise is therefore always followed by a snapshot, and that
|
|
925
|
+
snapshot is the answer; Omni waits for it rather than calling `snapshot()` itself. While the
|
|
926
|
+
transport is up, a result says the provider accepted the command, and the event that follows —
|
|
927
|
+
`task-updated`, `break-state` — says what it did.
|
|
921
928
|
|
|
922
929
|
A command therefore carries no key. The provider names its own records — a task, a lead request, a
|
|
923
930
|
member — and Omni refers to them by those names; **Omni never asks a provider to remember a name
|
|
@@ -934,6 +941,11 @@ and the agent is on it; had nothing been placed, nothing arrives and the agent d
|
|
|
934
941
|
Omni must not do is dial again on the agent's behalf — the one outcome worse than a lost answer is
|
|
935
942
|
two phones ringing at the customer.
|
|
936
943
|
|
|
944
|
+
An agent presses Hold while the provider is reconnecting. Nothing is queued at either end: the
|
|
945
|
+
adapter answers `failed` with `omni.unavailable`, Omni shows the refusal, and the agent presses
|
|
946
|
+
again once the provider is `active` — against the state as it is then, rather than a held press
|
|
947
|
+
fired into a state that has moved on.
|
|
948
|
+
|
|
937
949
|
### 7. Work is pulled, never pushed
|
|
938
950
|
|
|
939
951
|
Allocate only within the concurrent capacity Omni has stated for this agent.
|
|
@@ -2207,8 +2219,8 @@ answer and ask again when they want to. `decisionReason` may carry the words att
|
|
|
2207
2219
|
decision, but `approval` does not remain denied.
|
|
2208
2220
|
|
|
2209
2221
|
A provider reports `starting-after-task` only after Omni commits a `granted` request while
|
|
2210
|
-
work is still active. Omni does not
|
|
2211
|
-
it; it
|
|
2222
|
+
work is still active. Omni does not send the request again, because asking again would not move
|
|
2223
|
+
it; it sends the commit again only from a reconnect snapshot that shows the grant still standing.
|
|
2212
2224
|
|
|
2213
2225
|
`accepting: false` is what lets Omni withdraw the control rather than let an agent ask and be
|
|
2214
2226
|
refused. A `BreakReason` marked `alwaysAvailable` survives it: a mandatory rest period is not
|
|
@@ -2425,10 +2437,13 @@ Omni coordinates one attempt as follows:
|
|
|
2425
2437
|
causes Omni to take the cancel path.
|
|
2426
2438
|
4. If every participant reports `granted`, durably choose commit, enter `committing-break`,
|
|
2427
2439
|
and send `commitBreak()` to every participant. A provider then stops offering new work
|
|
2428
|
-
and reports `starting-after-task` or `in-effect`.
|
|
2429
|
-
|
|
2430
|
-
|
|
2431
|
-
|
|
2440
|
+
and reports `starting-after-task` or `in-effect`. The **commit bound** — ten seconds from the
|
|
2441
|
+
decision, tunable per deployment — decides who is kept: a participant that has not applied the
|
|
2442
|
+
commit by then, still `granted` or unreachable, is set aside as unreconciled and the break
|
|
2443
|
+
begins without it. `in-effect` decides `on-break`: Omni enters it once every kept participant
|
|
2444
|
+
reports `in-effect`. A kept participant reporting `starting-after-task` has applied the commit
|
|
2445
|
+
and is finishing a task; the bound is on delivery, not on that task. Omni shows the break as
|
|
2446
|
+
settled and beginning when the task ends, and offers no cancel, because the commit is durable.
|
|
2432
2447
|
5. If any participant fails or denies the request, cannot be reconciled within the bounded
|
|
2433
2448
|
decision timeout, or the agent cancels before commit, durably choose cancel and enter
|
|
2434
2449
|
`cancelling-break`. Send `cancelBreak()` to every participant still reporting
|
|
@@ -2467,7 +2482,13 @@ snapshot before anything else, and the snapshot decides:
|
|
|
2467
2482
|
against an agent who is already on break elsewhere. **A new login is this case too**: the grant
|
|
2468
2483
|
belonged to the old session.
|
|
2469
2484
|
|
|
2470
|
-
The provider must not offer work in the meantime, and the commit is what stops it
|
|
2485
|
+
The provider must not offer work in the meantime, and the commit is what stops it; Omni gives it
|
|
2486
|
+
no capacity until it is reconciled.
|
|
2487
|
+
|
|
2488
|
+
**If the agent has already ended the break elsewhere, the attempt is over** and the returning
|
|
2489
|
+
provider is reconciled to that instead: still `granted` gets `cancelBreak()`, because committing
|
|
2490
|
+
would stop an agent who is working again; `starting-after-task` or `in-effect` gets `endBreak()`.
|
|
2491
|
+
Never rolling back is about a break that is still on, not one the agent has finished.
|
|
2471
2492
|
|
|
2472
2493
|
Omni may tell the agent which platforms the break has not yet reached, as it already does when a
|
|
2473
2494
|
break cannot be paired across every provider.
|
|
@@ -2833,10 +2854,18 @@ Applies a `TaskCommandRequest` to one provider-local task.
|
|
|
2833
2854
|
- `applied` confirms the side effect completed. `failed` confirms it did **not**, with a typed
|
|
2834
2855
|
`ProtocolFailure`; a provider that will not and one that cannot report the same shape, and `code`
|
|
2835
2856
|
says which.
|
|
2857
|
+
- A command sent while `provider-status` is not `active` answers `failed` with `omni.unavailable`.
|
|
2858
|
+
Neither Omni nor the adapter queues it.
|
|
2859
|
+
- **A command that asks for a state answers `applied` when that state holds, whoever brought it
|
|
2860
|
+
about; a command that acts answers `failed` when it cannot act.** Declining a lead request
|
|
2861
|
+
already gone is `applied`; joining one already gone is `failed`, since nobody joined. A lead
|
|
2862
|
+
deciding a member's break that another lead has already decided is `applied` when the decisions
|
|
2863
|
+
agree and `failed`, saying so in `message`, when they differ. `commitBreak()` on a break already
|
|
2864
|
+
in effect is `committed` for the same reason.
|
|
2836
2865
|
- **A settled result is a fact; an unsettled promise is not.** Transport uncertainty may reject the
|
|
2837
|
-
promise with no result at all, and that means *unknown*, not *failed
|
|
2838
|
-
|
|
2839
|
-
happen.
|
|
2866
|
+
promise with no result at all, and that means *unknown*, not *failed*, and a snapshot follows —
|
|
2867
|
+
see **An unsettled result is unknown**. `failed` must never be returned for something the
|
|
2868
|
+
provider is unsure of, because Omni will show the agent it did not happen.
|
|
2840
2869
|
|
|
2841
2870
|
### `ProtocolFailure`
|
|
2842
2871
|
|
|
@@ -2858,7 +2887,7 @@ react rather than only display the message:
|
|
|
2858
2887
|
| `omni.task-not-found` | The provider-local task id is unknown, typically after the task already ended. |
|
|
2859
2888
|
| `omni.destination-not-permitted` | The dial or transfer destination violates the provider's policy. |
|
|
2860
2889
|
| `omni.rate-limited` | The action was throttled. Pair with `retryAfterMs`. |
|
|
2861
|
-
| `omni.unavailable` | The provider is temporarily unable to serve the action
|
|
2890
|
+
| `omni.unavailable` | The provider is temporarily unable to serve the action, including any command sent while `provider-status` is not `active`. |
|
|
2862
2891
|
| `omni.break-already-committed` | Cancellation lost the commit/cancel race; Omni must finish commit recovery. |
|
|
2863
2892
|
|
|
2864
2893
|
They are published as `OMNI_FAILURE_CODES`.
|
|
@@ -2915,9 +2944,10 @@ healthy provider from a dead one.
|
|
|
2915
2944
|
|
|
2916
2945
|
#### Requesting a resync
|
|
2917
2946
|
|
|
2918
|
-
Omni
|
|
2919
|
-
|
|
2920
|
-
|
|
2947
|
+
Omni calls `snapshot()` at connect; after that, snapshots come to it — on reconnect, and when the
|
|
2948
|
+
provider asks. It may still call `snapshot()` at any time, but never to learn a command's fate:
|
|
2949
|
+
the reconnect snapshot already carries it. `reason: "provider-requested"` covers the opposite
|
|
2950
|
+
direction — the provider asking Omni to reconcile — and neither replaces the other.
|
|
2921
2951
|
|
|
2922
2952
|
### `snapshot`
|
|
2923
2953
|
|
|
@@ -2959,9 +2989,9 @@ reasons, retry details, and any imposed break.
|
|
|
2959
2989
|
For a multi-provider attempt, "every provider" is the participant set frozen when the attempt
|
|
2960
2990
|
entered `requesting-break`. Omni commits only after every participant reports `granted` —
|
|
2961
2991
|
that one is unconditional, because nothing has stopped yet and waiting costs only time. It enters
|
|
2962
|
-
`on-break` once every participant
|
|
2963
|
-
|
|
2964
|
-
|
|
2992
|
+
`on-break` once every kept participant reports `in-effect`; the commit bound decides who is kept,
|
|
2993
|
+
setting aside a participant that has not applied the commit rather than holding a break that has
|
|
2994
|
+
already begun elsewhere. Otherwise it follows the two-phase rules under **Coordinating a
|
|
2965
2995
|
multi-provider break**.
|
|
2966
2996
|
|
|
2967
2997
|
### `task-offered`
|