@xema/omni-protocol 0.1.12 → 0.1.14
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 +2 -1
- package/dist/index.d.ts +12 -1
- package/dist/index.js +12 -0
- package/dist/testing.d.ts +27 -0
- package/dist/testing.js +53 -5
- package/dist/validation.js +6 -0
- package/guide.md +33 -10
- package/package.json +1 -5
- package/dist/design.d.ts +0 -49
- package/dist/design.js +0 -27
package/README.md
CHANGED
|
@@ -70,7 +70,8 @@ expect(result.disconnectWasClean).toBe(true);
|
|
|
70
70
|
```
|
|
71
71
|
|
|
72
72
|
Run the contract scenarios beside it — authentication restore and expiry, capability withdrawal,
|
|
73
|
-
reconnect with missed assignments, break denial and retry,
|
|
73
|
+
reconnect with missed assignments, break denial and retry, a break asked for on a task, who a
|
|
74
|
+
break asks, wrap timeout, browser isolation.
|
|
74
75
|
|
|
75
76
|
> **Assert both directions.** Every helper rejects a violating input as well as accepting a
|
|
76
77
|
> conforming one. A suite that only asserts "this conforming case does not throw" passes unchanged
|
package/dist/index.d.ts
CHANGED
|
@@ -137,7 +137,11 @@ export interface AuthenticationFailure {
|
|
|
137
137
|
}
|
|
138
138
|
/** What a lead may do with their team. Declared by presence. */
|
|
139
139
|
export interface TeamCapabilities {
|
|
140
|
-
/**
|
|
140
|
+
/**
|
|
141
|
+
* This lead may act on their team's breaks through `executeTeamBreak` -- place, release, decide,
|
|
142
|
+
* set policy -- as far as the provider supports; a command it lacks answers
|
|
143
|
+
* `omni.capability-not-enabled`. Requires `executeTeamBreak`.
|
|
144
|
+
*/
|
|
141
145
|
breakControl?: true;
|
|
142
146
|
/** This lead may join a member's call on request. Requires `executeTeamConsult`. */
|
|
143
147
|
consultControl?: true;
|
|
@@ -998,6 +1002,13 @@ export interface BrowserSessionKeyInput {
|
|
|
998
1002
|
taskType: string;
|
|
999
1003
|
browser: TaskBrowser;
|
|
1000
1004
|
}
|
|
1005
|
+
/**
|
|
1006
|
+
* Whether two logins declare the same capabilities, field by field. The comparison an adapter
|
|
1007
|
+
* makes before republishing `authenticated`: capabilities are current, not fixed, and the natural
|
|
1008
|
+
* guard -- comparing the identity -- never fires on a demotion, because the thing that changed is
|
|
1009
|
+
* not the thing being compared. Key order does not matter, and `team: {}` is not `team` absent.
|
|
1010
|
+
*/
|
|
1011
|
+
export declare function sameCapabilities(a: SessionCapabilities, b: SessionCapabilities): boolean;
|
|
1001
1012
|
/**
|
|
1002
1013
|
* The storage-profile key a reusing browser shares, or `undefined` where it shares nothing.
|
|
1003
1014
|
*
|
package/dist/index.js
CHANGED
|
@@ -128,6 +128,18 @@ export const HANDLING_STEPS_WITH_A_PERSON = [
|
|
|
128
128
|
export function handlingStepExpectsAPerson(step) {
|
|
129
129
|
return HANDLING_STEPS_WITH_A_PERSON.includes(step);
|
|
130
130
|
}
|
|
131
|
+
/**
|
|
132
|
+
* Whether two logins declare the same capabilities, field by field. The comparison an adapter
|
|
133
|
+
* makes before republishing `authenticated`: capabilities are current, not fixed, and the natural
|
|
134
|
+
* guard -- comparing the identity -- never fires on a demotion, because the thing that changed is
|
|
135
|
+
* not the thing being compared. Key order does not matter, and `team: {}` is not `team` absent.
|
|
136
|
+
*/
|
|
137
|
+
export function sameCapabilities(a, b) {
|
|
138
|
+
return a.breaks === b.breaks &&
|
|
139
|
+
(a.team === undefined) === (b.team === undefined) &&
|
|
140
|
+
a.team?.breakControl === b.team?.breakControl &&
|
|
141
|
+
a.team?.consultControl === b.team?.consultControl;
|
|
142
|
+
}
|
|
131
143
|
/**
|
|
132
144
|
* The storage-profile key a reusing browser shares, or `undefined` where it shares nothing.
|
|
133
145
|
*
|
package/dist/testing.d.ts
CHANGED
|
@@ -95,6 +95,33 @@ export declare function assertReconnectWithMissedAssignments<C extends Channel>(
|
|
|
95
95
|
* both.
|
|
96
96
|
*/
|
|
97
97
|
export declare function assertDeniedAndRetriedBreak(approvals: readonly BreakApproval[]): void;
|
|
98
|
+
/** One provider as the host sees it when freezing a break attempt's participant set. */
|
|
99
|
+
export interface BreakCandidate {
|
|
100
|
+
id: string;
|
|
101
|
+
authentication: AuthenticationState["status"];
|
|
102
|
+
/** Whether the agent can currently receive work from it: connected, with a capacity stated. */
|
|
103
|
+
holdsCapacity: boolean;
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* The participant set of a break attempt is every connected provider from which the agent can
|
|
107
|
+
* currently receive work. A provider whose login is not usable -- `expired` above all -- is not
|
|
108
|
+
* one, whatever else is true of it: nothing can be asked of it, and a host that waits on it
|
|
109
|
+
* stalls the break for everyone. A usable provider holding capacity is one, and cannot be left
|
|
110
|
+
* out. `refreshing` is usable: identity and capabilities remain available and work continues.
|
|
111
|
+
*/
|
|
112
|
+
export declare function assertBreakParticipants(candidates: readonly BreakCandidate[], participants: readonly string[]): void;
|
|
113
|
+
/** One published moment of a break asked for on a task: the approval, and how many tasks were outstanding. */
|
|
114
|
+
export interface BreakOnTaskStep {
|
|
115
|
+
approval: BreakApproval;
|
|
116
|
+
outstanding: number;
|
|
117
|
+
}
|
|
118
|
+
/**
|
|
119
|
+
* A break asked for on a task begins when the work ends. `steps` is the sequence the provider
|
|
120
|
+
* published, first to last: the request is made while work is outstanding, the commit is reported
|
|
121
|
+
* as `starting-after-task` while it remains, and `in-effect` arrives only once nothing is
|
|
122
|
+
* outstanding -- never beside a task, and never later than the step that has none.
|
|
123
|
+
*/
|
|
124
|
+
export declare function assertBreakBeginsAfterTask(steps: readonly BreakOnTaskStep[]): void;
|
|
98
125
|
/**
|
|
99
126
|
* Validates the deadline derived from media end and the task's fixed wrap allowance.
|
|
100
127
|
*
|
package/dist/testing.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { browserSessionKey, } from "./index.js";
|
|
1
|
+
import { browserSessionKey, sameCapabilities, } from "./index.js";
|
|
2
2
|
import { assertNoViolations, validateAuthenticationState, validateEventEnvelope, validateManifest, validateSnapshot, } from "./validation.js";
|
|
3
3
|
export { ProtocolConformanceError, assertNoViolations } from "./validation.js";
|
|
4
4
|
/**
|
|
@@ -313,10 +313,6 @@ function publishesUserIds(snapshot) {
|
|
|
313
313
|
return false;
|
|
314
314
|
return snapshot.tasks.some(task => Array.isArray(task?.handlingHistory) && task.handlingHistory.some(step => step?.by !== undefined));
|
|
315
315
|
}
|
|
316
|
-
const sameCapabilities = (a, b) => a.breaks === b.breaks &&
|
|
317
|
-
(a.team === undefined) === (b.team === undefined) &&
|
|
318
|
-
a.team?.breakControl === b.team?.breakControl &&
|
|
319
|
-
a.team?.consultControl === b.team?.consultControl;
|
|
320
316
|
/**
|
|
321
317
|
* `refreshing` carries the identity and capabilities of the login it refreshes. A change to
|
|
322
318
|
* either is published as `authenticated`; a different identity is a new login.
|
|
@@ -491,6 +487,58 @@ export function assertDeniedAndRetriedBreak(approvals) {
|
|
|
491
487
|
throw new Error(`Break retry scenario must end granted or in effect, ended ${String(last)}`);
|
|
492
488
|
}
|
|
493
489
|
}
|
|
490
|
+
const usableLogin = (status) => status === "authenticated" || status === "refreshing";
|
|
491
|
+
/**
|
|
492
|
+
* The participant set of a break attempt is every connected provider from which the agent can
|
|
493
|
+
* currently receive work. A provider whose login is not usable -- `expired` above all -- is not
|
|
494
|
+
* one, whatever else is true of it: nothing can be asked of it, and a host that waits on it
|
|
495
|
+
* stalls the break for everyone. A usable provider holding capacity is one, and cannot be left
|
|
496
|
+
* out. `refreshing` is usable: identity and capabilities remain available and work continues.
|
|
497
|
+
*/
|
|
498
|
+
export function assertBreakParticipants(candidates, participants) {
|
|
499
|
+
const chosen = new Set(participants);
|
|
500
|
+
for (const id of participants) {
|
|
501
|
+
if (!candidates.some(candidate => candidate.id === id))
|
|
502
|
+
throw new Error(`${id} is not a provider the host knows`);
|
|
503
|
+
}
|
|
504
|
+
for (const candidate of candidates) {
|
|
505
|
+
const expected = usableLogin(candidate.authentication) && candidate.holdsCapacity;
|
|
506
|
+
if (expected && !chosen.has(candidate.id)) {
|
|
507
|
+
throw new Error(`${candidate.id} can give the agent work and must be a participant`);
|
|
508
|
+
}
|
|
509
|
+
if (!expected && chosen.has(candidate.id)) {
|
|
510
|
+
const why = usableLogin(candidate.authentication) ? "holds no capacity" : `is ${candidate.authentication}`;
|
|
511
|
+
throw new Error(`${candidate.id} ${why} and is not a participant: nothing can be asked of it`);
|
|
512
|
+
}
|
|
513
|
+
}
|
|
514
|
+
}
|
|
515
|
+
/**
|
|
516
|
+
* A break asked for on a task begins when the work ends. `steps` is the sequence the provider
|
|
517
|
+
* published, first to last: the request is made while work is outstanding, the commit is reported
|
|
518
|
+
* as `starting-after-task` while it remains, and `in-effect` arrives only once nothing is
|
|
519
|
+
* outstanding -- never beside a task, and never later than the step that has none.
|
|
520
|
+
*/
|
|
521
|
+
export function assertBreakBeginsAfterTask(steps) {
|
|
522
|
+
const asked = steps.findIndex(step => step.approval === "awaiting-decision" || step.approval === "granted");
|
|
523
|
+
if (asked < 0)
|
|
524
|
+
throw new Error("Break-on-task scenario requires a request");
|
|
525
|
+
if ((steps[asked]?.outstanding ?? 0) < 1)
|
|
526
|
+
throw new Error("Break-on-task scenario requires the request to be made while a task is outstanding");
|
|
527
|
+
const committed = steps.findIndex((step, index) => index > asked && step.approval === "starting-after-task");
|
|
528
|
+
if (committed < 0)
|
|
529
|
+
throw new Error("A break committed on a task is reported as starting-after-task while the work remains");
|
|
530
|
+
steps.forEach((step, index) => {
|
|
531
|
+
if (step.approval === "in-effect" && step.outstanding > 0) {
|
|
532
|
+
throw new Error(`steps[${index}] reports in-effect with ${step.outstanding} task(s) outstanding: a break begins when the work ends`);
|
|
533
|
+
}
|
|
534
|
+
if (step.approval === "starting-after-task" && step.outstanding < 1) {
|
|
535
|
+
throw new Error(`steps[${index}] reports starting-after-task with nothing outstanding: the break should have begun`);
|
|
536
|
+
}
|
|
537
|
+
});
|
|
538
|
+
if (steps.at(-1)?.approval !== "in-effect") {
|
|
539
|
+
throw new Error(`Break-on-task scenario must end in effect, ended ${String(steps.at(-1)?.approval)}`);
|
|
540
|
+
}
|
|
541
|
+
}
|
|
494
542
|
/**
|
|
495
543
|
* Validates the deadline derived from media end and the task's fixed wrap allowance.
|
|
496
544
|
*
|
package/dist/validation.js
CHANGED
|
@@ -808,6 +808,12 @@ export function validateSnapshot(snapshot, manifest, path = "snapshot", context
|
|
|
808
808
|
}
|
|
809
809
|
});
|
|
810
810
|
}
|
|
811
|
+
// A break in effect begins when the work ends, so it holds no task. A snapshot reporting both
|
|
812
|
+
// describes a state the agent cannot be in, whichever half is stale.
|
|
813
|
+
if (isPlainObject(snapshot.break) && snapshot.break.approval === "in-effect"
|
|
814
|
+
&& Array.isArray(snapshot.tasks) && snapshot.tasks.length > 0) {
|
|
815
|
+
into.add("break.in-effect.tasks", `${path}.tasks`, "a break in effect holds no task: it begins when the work ends, and until then the state is starting-after-task");
|
|
816
|
+
}
|
|
811
817
|
// Presence is the permission, and it cuts both ways: data a provider never declared a
|
|
812
818
|
// capability for is data Omni would show against a control the agent does not have.
|
|
813
819
|
const idle = isPlainObject(manifest) && isPlainObject(manifest.idleCapabilities) ? manifest.idleCapabilities : {};
|
package/guide.md
CHANGED
|
@@ -1002,7 +1002,6 @@ Request/response polling does not have those properties and is not a transport f
|
|
|
1002
1002
|
| `@xema/omni-protocol` | Provider adapter contract and shared domain types |
|
|
1003
1003
|
| `@xema/omni-protocol/testing` | Adapter conformance helpers |
|
|
1004
1004
|
| `@xema/omni-protocol/validation` | Runtime validators Omni and adapters both use to reject malformed data |
|
|
1005
|
-
| `@xema/omni-protocol/design` | Host design-language integration. Specified separately; no part of it is a provider surface. |
|
|
1006
1005
|
|
|
1007
1006
|
## Declaring an adapter
|
|
1008
1007
|
|
|
@@ -1352,7 +1351,7 @@ them from what arrives later.
|
|
|
1352
1351
|
| --- | --- |
|
|
1353
1352
|
| `breaks` | This login may request a break. Requires the four break methods on the connection. |
|
|
1354
1353
|
| `team` | This login leads a team. The provider publishes a `TeamRoster` to it on every snapshot — `[]` when nobody is in it — and to nobody else. |
|
|
1355
|
-
| `team.breakControl` | This lead
|
|
1354
|
+
| `team.breakControl` | This lead may act on their team's breaks through `executeTeamBreak` — place, release, decide, 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`. |
|
|
1356
1355
|
| `team.consultControl` | This lead may join a member's call on request. Requires `executeTeamConsult`. |
|
|
1357
1356
|
|
|
1358
1357
|
A session action is available only when both the capability and Omni provisioning permit it.
|
|
@@ -1366,7 +1365,10 @@ roster arrives, and nothing for anybody else — and withdraws it on the next re
|
|
|
1366
1365
|
capability goes. A command that arrives after its capability was withdrawn is answered `failed`
|
|
1367
1366
|
with `omni.capability-not-enabled`: the provider names it, so Omni never has to infer from a
|
|
1368
1367
|
capability change it may not have rendered yet that "you are no longer a lead" is the message
|
|
1369
|
-
rather than "that did not work".
|
|
1368
|
+
rather than "that did not work". One trap for adapter authors: the natural guard on a republish
|
|
1369
|
+
compares the identity, and a demotion does not touch it — compare the capabilities, field by
|
|
1370
|
+
field, which is what `sameCapabilities()` does. The thing that changed is not the thing you are
|
|
1371
|
+
comparing, and `assertCapabilityWithdrawal` is the test that catches a guard which never fires.
|
|
1370
1372
|
|
|
1371
1373
|
### Starting authentication
|
|
1372
1374
|
|
|
@@ -2257,7 +2259,7 @@ rendering one as the other tells an agent to wait for somebody who is never comi
|
|
|
2257
2259
|
| `awaiting-decision` | A person has to decide. The agent is waiting on somebody. |
|
|
2258
2260
|
| `granted` | A person decided yes. Omni may now tell this provider to stop the agent; until it does, work continues normally, and this says nothing about why Omni has not. |
|
|
2259
2261
|
| `starting-after-task` | Omni has told the provider to stop; the break begins when the current task ends. No new work arrives meanwhile, and nobody needs to act. |
|
|
2260
|
-
| `in-effect` | The agent is on the break now. |
|
|
2262
|
+
| `in-effect` | The agent is on the break now. It holds no task: a break begins when the work ends, so a snapshot reporting `in-effect` beside a task is refused as `break.in-effect.tasks`. |
|
|
2261
2263
|
|
|
2262
2264
|
A denial is a decision, not a standing approval state. The provider transitions the request directly
|
|
2263
2265
|
to `not-requested`; Omni returns the agent to idle and never asks again on their behalf. They saw the
|
|
@@ -2362,6 +2364,11 @@ Requests permission to stop the agent later; it does not itself stop work. The p
|
|
|
2362
2364
|
offering work and reports `awaiting-decision` or `granted` through `break-state` events. If the request is denied, the
|
|
2363
2365
|
provider reports `not-requested` directly, with `decisionReason` when one was supplied.
|
|
2364
2366
|
|
|
2367
|
+
**An agent asks from anywhere — idle or on a task** — and Omni offers the request in the task
|
|
2368
|
+
workspace as it does on the idle dashboard. Asked on a task, the break is decided and committed like
|
|
2369
|
+
any other and begins when the work ends: that is `starting-after-task`, and
|
|
2370
|
+
`assertBreakBeginsAfterTask` is the scenario that holds a provider to it.
|
|
2371
|
+
|
|
2365
2372
|
#### Break reasons
|
|
2366
2373
|
|
|
2367
2374
|
A provider that defines not-ready reason codes publishes them on `Snapshot.break.reasons`,
|
|
@@ -2477,6 +2484,11 @@ Omni coordinates one attempt as follows:
|
|
|
2477
2484
|
|
|
2478
2485
|
1. Freeze the participant set to every connected provider from which the agent can currently
|
|
2479
2486
|
receive work. A provider joining during the attempt is given no capacity until it finishes.
|
|
2487
|
+
A provider whose authentication is `expired` is not one the agent can receive work from and is
|
|
2488
|
+
not a participant: nothing is asked of it, the break proceeds without it, and when the login
|
|
2489
|
+
is restored it is reconciled from its snapshot as a set-aside provider is, with no capacity
|
|
2490
|
+
until then. `refreshing` keeps a provider in — its identity and capabilities remain available
|
|
2491
|
+
and work continues. `assertBreakParticipants` holds a host to this set.
|
|
2480
2492
|
2. Enter `requesting-break`. Keep the agent's normal capacity in place throughout this phase.
|
|
2481
2493
|
3. Send one `requestBreak` to every participant. A provider reports `awaiting-decision` or
|
|
2482
2494
|
`granted`; neither state stops work. A denial transitions directly to `not-requested` and
|
|
@@ -2702,6 +2714,12 @@ provider whose lead is already at the ceiling answers the join `failed`.
|
|
|
2702
2714
|
they joined -- is answered `failed`, whatever their ceiling; the request stands for another lead,
|
|
2703
2715
|
or until it is withdrawn or declined.
|
|
2704
2716
|
|
|
2717
|
+
**A lead on a break does not join.** A break is a reported state in which the agent is not working,
|
|
2718
|
+
and a join is work. Omni offers Join to a lead only while their own `BreakState.approval` is neither
|
|
2719
|
+
`starting-after-task` nor `in-effect` -- a committed break waiting for the lead's current work to
|
|
2720
|
+
finish is not given more -- and a provider answers a `join` from a lead on such a break `failed`.
|
|
2721
|
+
The request stands for another lead, as it does when this one is already on a call.
|
|
2722
|
+
|
|
2705
2723
|
**On `decline`, or a request the agent withdraws with `{ type: "lead", action: "cancel" }`, the
|
|
2706
2724
|
provider clears `lead` from the agent's task** and drops the request from every roster. Nothing
|
|
2707
2725
|
else changes; the agent is still on the call.
|
|
@@ -3138,6 +3156,12 @@ Use it for every `UserId` — `handlingHistory[].by`, roster members, `memberId`
|
|
|
3138
3156
|
lead command, `ImposedBreak.by`. A bare one is only ever compared against another from the **same** provider; anything
|
|
3139
3157
|
wider goes through this key.
|
|
3140
3158
|
|
|
3159
|
+
### `sameCapabilities(a, b)`
|
|
3160
|
+
|
|
3161
|
+
Whether two logins declare the same capabilities, field by field — key order aside, and with
|
|
3162
|
+
`team: {}` distinct from `team` absent. It is the comparison an adapter makes before republishing
|
|
3163
|
+
`authenticated`, and the one `exerciseAdapter` holds `refreshing` to.
|
|
3164
|
+
|
|
3141
3165
|
## Runtime validation
|
|
3142
3166
|
|
|
3143
3167
|
Structural rules in this document are executable through the runtime validators Omni applies to
|
|
@@ -3233,10 +3257,12 @@ cannot be established from TypeScript structure alone.
|
|
|
3233
3257
|
| Helper | Contract checked |
|
|
3234
3258
|
| --- | --- |
|
|
3235
3259
|
| `assertCapabilityWithdrawal(states, snapshot, manifest)` | A capability withdrawn by a later `authenticated` state is gone from the next snapshot: no roster for a login that no longer leads, no requests for one that may no longer join. Every state is validated on the way, `refreshing` must carry the login over, and the sequence passes only through usable states. |
|
|
3236
|
-
| `assertCommandRefusedAfterWithdrawal(result)` | A command that arrives after its capability was withdrawn fails with `omni.capability-not-enabled`, named by the provider. |
|
|
3260
|
+
| `assertCommandRefusedAfterWithdrawal(result)` | A command that arrives after its capability was withdrawn fails with `omni.capability-not-enabled`, named by the provider. The same assertion serves a command the provider never supported under a capability it declares. |
|
|
3237
3261
|
| `assertReached(result, subjects)` | The exercise met every subject named; throws listing those it did not. Pair it with a clean `exerciseAdapter` result. |
|
|
3238
3262
|
| `assertAuthenticationRestoreAndExpiry(states)` | A restored authenticated session can refresh and ends in expiry. Every state is validated. |
|
|
3239
3263
|
| `assertReconnectWithMissedAssignments(before, reconnect, ids)` | A reconnect snapshot restores assignments received while offline. |
|
|
3264
|
+
| `assertBreakParticipants(candidates, participants)` | A break attempt asks every usable provider holding capacity, `refreshing` included, and nothing of a provider whose login is `expired`. |
|
|
3265
|
+
| `assertBreakBeginsAfterTask(steps)` | A break asked for on a task is committed as `starting-after-task` while work remains and reaches `in-effect` only once nothing is outstanding — never beside a task, never later than the step that has none. |
|
|
3240
3266
|
| `assertDeniedAndRetriedBreak(states)` | A denial transitions directly to `not-requested`; a later request can still be granted. |
|
|
3241
3267
|
| `assertWrapTimeout(task, mediaEndedAt, deadline, toleranceMs?)` | The wrap deadline equals media end plus the task allowance, within a tolerance that defaults to 1000ms; a task with no allowance has no deadline, and one observed is the violation. |
|
|
3242
3268
|
| `assertBrowserIsolationAndReuse(left, right, expected)` | Browser reuse follows only the declared isolation scheme. |
|
|
@@ -3250,11 +3276,8 @@ Adapters should run the relevant scenarios against deterministic test state befo
|
|
|
3250
3276
|
|
|
3251
3277
|
## A provider does not style the workspace
|
|
3252
3278
|
|
|
3253
|
-
|
|
3254
|
-
|
|
3255
|
-
is why no part of it is declared under **Shapes**.
|
|
3256
|
-
|
|
3257
|
-
What belongs in this contract is the boundary. A provider says what a control **is** through its
|
|
3279
|
+
How a deployment themes Omni is the host's concern and is specified with the host, not here. What
|
|
3280
|
+
belongs in this contract is the boundary. A provider says what a control **is** through its
|
|
3258
3281
|
capabilities and what its work is **called** through `phaseLabels` and `taskTypePresentation`; how
|
|
3259
3282
|
any of it is drawn is Omni's. A task cannot select a design language, inject a component, or
|
|
3260
3283
|
override the agent's theme and font preferences.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@xema/omni-protocol",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.14",
|
|
4
4
|
"description": "The Omni protocol: the contract every provider adapter implements",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -18,10 +18,6 @@
|
|
|
18
18
|
"types": "./dist/index.d.ts",
|
|
19
19
|
"import": "./dist/index.js"
|
|
20
20
|
},
|
|
21
|
-
"./design": {
|
|
22
|
-
"types": "./dist/design.d.ts",
|
|
23
|
-
"import": "./dist/design.js"
|
|
24
|
-
},
|
|
25
21
|
"./testing": {
|
|
26
22
|
"types": "./dist/testing.d.ts",
|
|
27
23
|
"import": "./dist/testing.js"
|
package/dist/design.d.ts
DELETED
|
@@ -1,49 +0,0 @@
|
|
|
1
|
-
export type ThemePreference = "system" | "light" | "dark";
|
|
2
|
-
export type ResolvedTheme = Exclude<ThemePreference, "system">;
|
|
3
|
-
export type Density = "compact" | "comfortable" | "spacious";
|
|
4
|
-
/** Semantic values consumed by Omni's layout, independent of a CSS framework. */
|
|
5
|
-
export interface DesignTokens {
|
|
6
|
-
accent: string;
|
|
7
|
-
accentText: string;
|
|
8
|
-
surface: string;
|
|
9
|
-
surfaceMuted: string;
|
|
10
|
-
selected: string;
|
|
11
|
-
text: string;
|
|
12
|
-
mutedText: string;
|
|
13
|
-
border: string;
|
|
14
|
-
info: string;
|
|
15
|
-
success: string;
|
|
16
|
-
warning: string;
|
|
17
|
-
danger: string;
|
|
18
|
-
radius: string;
|
|
19
|
-
radiusLarge: string;
|
|
20
|
-
shadow: string;
|
|
21
|
-
controlFont: string;
|
|
22
|
-
controlWeight: string;
|
|
23
|
-
controlTracking: string;
|
|
24
|
-
controlHeight: string;
|
|
25
|
-
}
|
|
26
|
-
export type ControlKind = "button" | "icon-button" | "checkbox" | "input" | "textarea" | "select" | "tabs" | "menu" | "badge" | "card" | "progress";
|
|
27
|
-
export interface DesignLanguageManifest {
|
|
28
|
-
id: string;
|
|
29
|
-
displayName: string;
|
|
30
|
-
supportedThemes: ReadonlyArray<ResolvedTheme>;
|
|
31
|
-
supportedControls: ReadonlyArray<ControlKind>;
|
|
32
|
-
defaultDensity: Density;
|
|
33
|
-
}
|
|
34
|
-
export interface DesignLanguage {
|
|
35
|
-
manifest: DesignLanguageManifest;
|
|
36
|
-
tokens: Record<ResolvedTheme, DesignTokens>;
|
|
37
|
-
}
|
|
38
|
-
/**
|
|
39
|
-
* A framework bridge can use any native control representation: an Angular
|
|
40
|
-
* component type, a React component, or an Omni DOM renderer. The protocol
|
|
41
|
-
* deliberately does not make one UI framework part of the ABI.
|
|
42
|
-
*/
|
|
43
|
-
export interface DesignLanguageAdapter<TControl = unknown> {
|
|
44
|
-
readonly language: DesignLanguage;
|
|
45
|
-
resolveControl(kind: ControlKind): TControl | Promise<TControl>;
|
|
46
|
-
}
|
|
47
|
-
export declare function defineDesignLanguage<TControl, T extends DesignLanguageAdapter<TControl>>(adapter: T): T;
|
|
48
|
-
/** Maps semantic tokens to the stable CSS custom properties understood by Omni. */
|
|
49
|
-
export declare function designTokenProperties(tokens: DesignTokens): Record<string, string>;
|
package/dist/design.js
DELETED
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
export function defineDesignLanguage(adapter) {
|
|
2
|
-
return adapter;
|
|
3
|
-
}
|
|
4
|
-
/** Maps semantic tokens to the stable CSS custom properties understood by Omni. */
|
|
5
|
-
export function designTokenProperties(tokens) {
|
|
6
|
-
return {
|
|
7
|
-
"--omni-accent": tokens.accent,
|
|
8
|
-
"--omni-accent-text": tokens.accentText,
|
|
9
|
-
"--omni-surface": tokens.surface,
|
|
10
|
-
"--omni-surface-muted": tokens.surfaceMuted,
|
|
11
|
-
"--omni-selected": tokens.selected,
|
|
12
|
-
"--omni-text": tokens.text,
|
|
13
|
-
"--omni-muted-text": tokens.mutedText,
|
|
14
|
-
"--omni-border": tokens.border,
|
|
15
|
-
"--omni-info": tokens.info,
|
|
16
|
-
"--omni-success": tokens.success,
|
|
17
|
-
"--omni-warning": tokens.warning,
|
|
18
|
-
"--omni-danger": tokens.danger,
|
|
19
|
-
"--omni-radius": tokens.radius,
|
|
20
|
-
"--omni-radius-large": tokens.radiusLarge,
|
|
21
|
-
"--omni-shadow": tokens.shadow,
|
|
22
|
-
"--omni-control-font": tokens.controlFont,
|
|
23
|
-
"--omni-control-weight": tokens.controlWeight,
|
|
24
|
-
"--omni-control-tracking": tokens.controlTracking,
|
|
25
|
-
"--omni-control-height": tokens.controlHeight,
|
|
26
|
-
};
|
|
27
|
-
}
|