@xema/omni-protocol 0.1.20 → 0.1.21

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 CHANGED
@@ -111,8 +111,8 @@ export interface Manifest<C extends Channel = Channel> {
111
111
  phaseLabels?: TaskPhaseLabels;
112
112
  /** Keyed by `taskType`. An entry replaces the channel default outright rather than merging. */
113
113
  taskTypePresentation?: Record<string, TaskTypePresentation>;
114
- /** The structure's tiers, relabelling or extending `DEFAULT_TIERS` by id. Omitted for the typical four. */
115
- tiers?: TierDeclaration[];
114
+ /** The organisation's whole ladder, stated outright, `person` included. Omitted for the typical four, `DEFAULT_TIERS`. */
115
+ orgTiers?: TierDeclaration[];
116
116
  }
117
117
  export interface SecretStore {
118
118
  get(key: string): Promise<string | undefined>;
@@ -530,7 +530,7 @@ export interface TaskAssisting {
530
530
  }
531
531
  /**
532
532
  * A tier of the organisation's structure, by the id its manifest declares -- or one of the four
533
- * every organisation has, `DEFAULT_TIERS`, which a manifest relabels or adds to. The protocol
533
+ * a typical organisation has, `DEFAULT_TIERS`, when the manifest declares no ladder. The protocol
534
534
  * never describes the chain: which tiers a person passes through is the structure's to know.
535
535
  */
536
536
  export type Tier = string;
@@ -540,8 +540,8 @@ export interface TierDeclaration {
540
540
  label: string;
541
541
  }
542
542
  /**
543
- * The tiers a typical organisation has. A manifest that declares `tiers` relabels any of these
544
- * by id and may add its own; one that declares none has exactly these.
543
+ * The tiers a typical organisation has: the ladder in force when a manifest declares no
544
+ * `orgTiers`. A manifest that declares any states its whole ladder outright.
545
545
  */
546
546
  export declare const DEFAULT_TIERS: readonly [{
547
547
  readonly id: "org";
@@ -556,7 +556,7 @@ export declare const DEFAULT_TIERS: readonly [{
556
556
  readonly id: "person";
557
557
  readonly label: "You";
558
558
  }];
559
- /** The tiers in force for a manifest: the defaults, relabelled or extended by what it declares. */
559
+ /** The ladder in force for a manifest: exactly what it declares, or the defaults when it declares none. */
560
560
  export declare function effectiveTiers(declared: readonly TierDeclaration[] | undefined): TierDeclaration[];
561
561
  /**
562
562
  * Something the queue could allow, locked above the person: the tier that made it unchangeable,
package/dist/index.js CHANGED
@@ -51,8 +51,8 @@ export function isAllowedBrowserUrl(url) {
51
51
  }
52
52
  }
53
53
  /**
54
- * The tiers a typical organisation has. A manifest that declares `tiers` relabels any of these
55
- * by id and may add its own; one that declares none has exactly these.
54
+ * The tiers a typical organisation has: the ladder in force when a manifest declares no
55
+ * `orgTiers`. A manifest that declares any states its whole ladder outright.
56
56
  */
57
57
  export const DEFAULT_TIERS = [
58
58
  { id: "org", label: "Your organisation" },
@@ -60,12 +60,9 @@ export const DEFAULT_TIERS = [
60
60
  { id: "team", label: "Your team" },
61
61
  { id: "person", label: "You" },
62
62
  ];
63
- /** The tiers in force for a manifest: the defaults, relabelled or extended by what it declares. */
63
+ /** The ladder in force for a manifest: exactly what it declares, or the defaults when it declares none. */
64
64
  export function effectiveTiers(declared) {
65
- const byId = new Map(DEFAULT_TIERS.map(tier => [tier.id, tier]));
66
- for (const tier of declared ?? [])
67
- byId.set(tier.id, tier);
68
- return [...byId.values()];
65
+ return [...(declared ?? DEFAULT_TIERS)];
69
66
  }
70
67
  // ---------------------------------------------------------------------------
71
68
  // Task commands.
package/dist/testing.js CHANGED
@@ -169,7 +169,7 @@ export async function exerciseAdapter(adapter, context, options = {}) {
169
169
  let disconnectWasClean = false;
170
170
  try {
171
171
  authenticationState = await authentication.state();
172
- const tiers = effectiveTiers(adapter.manifest.tiers).map(tier => tier.id);
172
+ const tiers = effectiveTiers(adapter.manifest.orgTiers).map(tier => tier.id);
173
173
  violations.push(...validateAuthenticationState(authenticationState, "authentication", { tiers }));
174
174
  if (authenticationState.status !== "authenticated") {
175
175
  throw new Error(`Adapter contract exercise requires authenticated test state, received ${authenticationState.status}`);
@@ -311,25 +311,35 @@ export function validateManifest(manifest, path = "manifest") {
311
311
  }
312
312
  }
313
313
  }
314
- if (manifest.tiers !== undefined) {
315
- if (!Array.isArray(manifest.tiers)) {
316
- into.add("manifest.tiers.shape", `${path}.tiers`, "tiers must be an array when present");
314
+ if (manifest.orgTiers !== undefined) {
315
+ if (!Array.isArray(manifest.orgTiers)) {
316
+ into.add("manifest.orgTiers.shape", `${path}.orgTiers`, "orgTiers must be an array when present");
317
317
  }
318
318
  else {
319
319
  const ids = new Set();
320
- manifest.tiers.forEach((tier, index) => {
321
- const at = `${path}.tiers[${index}]`;
320
+ let wellFormed = true;
321
+ manifest.orgTiers.forEach((tier, index) => {
322
+ const at = `${path}.orgTiers[${index}]`;
322
323
  if (!isPlainObject(tier)) {
323
- into.add("manifest.tier.shape", at, "each tier must be an object with an id and a label");
324
+ into.add("manifest.orgTier.shape", at, "each tier must be an object with an id and a label");
325
+ wellFormed = false;
324
326
  return;
325
327
  }
326
- if (into.filled(tier.id, "manifest.tier.id", `${at}.id`, "a tier needs an id")) {
327
- if (ids.has(tier.id))
328
- into.add("manifest.tier.unique", `${at}.id`, `duplicate tier: ${tier.id}`);
328
+ if (into.filled(tier.id, "manifest.orgTier.id", `${at}.id`, "a tier needs an id")) {
329
+ if (ids.has(tier.id)) {
330
+ into.add("manifest.orgTier.unique", `${at}.id`, `duplicate tier: ${tier.id}`);
331
+ wellFormed = false;
332
+ }
329
333
  ids.add(tier.id);
330
334
  }
331
- into.filled(tier.label, "manifest.tier.label", `${at}.label`, "a tier needs the label a desk shows for it");
335
+ else
336
+ wellFormed = false;
337
+ if (!into.filled(tier.label, "manifest.orgTier.label", `${at}.label`, "a tier needs the label a desk shows for it"))
338
+ wellFormed = false;
332
339
  });
340
+ if (wellFormed && !ids.has("person")) {
341
+ into.add("manifest.orgTiers.person", `${path}.orgTiers`, "a declared ladder states the whole ladder and must include person, the subject of every resolution");
342
+ }
333
343
  }
334
344
  }
335
345
  if (manifest.taskTypePresentation !== undefined) {
@@ -488,7 +498,7 @@ function validateLockedByInto(value, rule, path, tiers, into) {
488
498
  if (!into.filled(value, rule, path, "lockedBy names the tier that locked it"))
489
499
  return;
490
500
  into.require(value !== "person", `${rule}.person`, path, "a person never locks their own value");
491
- into.require(tierIds(tiers).includes(value), `${rule}.unknown`, path, `${String(value)} is not a tier this manifest declares: the defaults are ${DEFAULT_TIER_IDS.join(", ")}`);
501
+ into.require(tierIds(tiers).includes(value), `${rule}.unknown`, path, `${String(value)} is not a tier this manifest declares: in force are ${tierIds(tiers).join(", ")}`);
492
502
  }
493
503
  /** `{ lockedBy, reason? }` standing in for a value: who locked it, and a reason if given. */
494
504
  function validateLockedInto(value, rule, path, tiers, into) {
@@ -512,9 +522,9 @@ function validateResolvedInto(value, rule, path, tiers, into) {
512
522
  function manifestTiers(manifest) {
513
523
  if (!isPlainObject(manifest))
514
524
  return undefined;
515
- const declared = Array.isArray(manifest.tiers)
516
- ? manifest.tiers.filter((tier) => isPlainObject(tier) && typeof tier.id === "string")
517
- : [];
525
+ const declared = Array.isArray(manifest.orgTiers)
526
+ ? manifest.orgTiers.filter((tier) => isPlainObject(tier) && typeof tier.id === "string")
527
+ : undefined;
518
528
  return effectiveTiers(declared).map(tier => tier.id);
519
529
  }
520
530
  const CUSTOM_RENDERS = membersOf({ inline: true, page: true });
package/guide.md CHANGED
@@ -176,7 +176,7 @@ type Manifest<C extends Channel = Channel> = {
176
176
  idleCapabilities?: IdleCapabilities<C>;
177
177
  phaseLabels?: TaskPhaseLabels;
178
178
  taskTypePresentation?: Record<string, TaskTypePresentation>;
179
- tiers?: TierDeclaration[];
179
+ orgTiers?: TierDeclaration[];
180
180
  };
181
181
  ```
182
182
 
@@ -708,13 +708,17 @@ the person settles the value for everyone below it, and where nothing enforces t
708
708
  tier that says anything wins. The protocol names a tier by the id the manifest declares for it and
709
709
  never describes the chain between them: which tiers a person passes through is the structure's to
710
710
  know. A typical organisation has four, and they are the defaults — `DEFAULT_TIERS`: `org`, `site`,
711
- `team`, `person`, each with the label a desk shows which `Manifest.tiers` relabels by id (a
712
- provider whose "site" means something else says what it means) or extends with tiers of its own;
713
- a manifest that declares none has exactly the four. `lockedBy` is any tier in force except
714
- `person`, who never locks their own value; `setBy` is any tier in force, or `provisioning`, the
715
- protocol's own word for "no tier has said anything and the provider's default applies". A host
716
- renders "who decided" from the declared labels and needs no others. What the wire carries is the
717
- resolution:
711
+ `team`, `person`, each with the label a desk shows. A structure that differs states its whole
712
+ ladder in `Manifest.orgTiers`, `person` included: what the list carries is in force, and what it
713
+ leaves out does not exist a structure with no site tier declares `org`, `team`, `person`, and
714
+ `site` is refused on its wire. A declared tier is one the provider's own store actually resolves
715
+ at: a label with no policy behind it decides nothing. A manifest that declares none has exactly
716
+ the four. `lockedBy` is any tier in force except `person`, who never locks their own value;
717
+ `setBy` is any tier in force, or `provisioning`, the protocol's word for "no tier has said
718
+ anything and the provider's own configuration supplied the value" — the provider speaking, never
719
+ Omni's provisioning file, which does not reach the wire. A host renders "who decided" from the
720
+ declared labels and needs no others, and validates every republished `authenticated` state
721
+ against them, not only the sign-in. What the wire carries is the resolution:
718
722
 
719
723
  - **`lockedBy`** — a tier above the person made this value theirs to keep. A person never locks
720
724
  their own value, and the queue is not a tier: what the queue does not allow at all is absent.
@@ -1349,7 +1353,7 @@ compile time.
1349
1353
  | `idleCapabilities` | Declares actions Omni may offer while the agent has no active task, such as voice dialing. Task controls do not belong here. |
1350
1354
  | `phaseLabels` | Optional static adapter-defined display names for canonical `TaskPhase` values. They cannot vary at runtime. |
1351
1355
  | `taskTypePresentation` | Optional static adapter-defined presentation keyed by exact `taskType`. It names the item and its optional agent-facing reference. |
1352
- | `tiers` | The structure's tiers as the provider calls them, each with the label a desk shows for "who decided". Relabels any of `DEFAULT_TIERS` by id and may add others; omitted for the typical four. See **Who decides what an agent may do**. |
1356
+ | `orgTiers` | The organisation's whole ladder as the provider calls it, each tier with the label a desk shows for "who decided". Stated outright, `person` included: what it leaves out does not exist. Omitted for the typical four, `DEFAULT_TIERS`. See **Who decides what an agent may do**. |
1353
1357
 
1354
1358
  ### Authentication methods
1355
1359
 
@@ -3568,7 +3572,7 @@ same exported checks are used by Omni and adapter tests so their interpretations
3568
3572
  | `validateScheduledActivity(activity)` | Required activity fields and start/end ordering. |
3569
3573
  | `validateHostReport(report)` | The host's own report as published to an adapter: `online`, and where there is audio, an input that is `ready` with the microphone and `flowing`, or `unavailable` with a reason and the failure that says why, and an output that is `ready` or `unavailable` with its failure. The harness validates whatever host a test hands the adapter; `stillHost(report)` builds one that never changes. |
3570
3574
  | `validateResult(result, method)` | What a connection method answered: the status it gives, a failure where the status says so and nowhere else, the failure's shape, and that an `omni.` code is one this contract names. |
3571
- | `validateAuthenticationState(state)` | The identity each state must carry, the capabilities a usable login declares, and the expiry that only `authenticated` may. |
3575
+ | `validateAuthenticationState(state)` | The identity each state must carry, the capabilities a usable login declares, and the expiry that only `authenticated` may. Omni applies it to every state a session publishes — the republished as much as the first. |
3572
3576
 
3573
3577
  Each returns `ProtocolViolation[]` rather than throwing, so a caller can report every problem at
3574
3578
  once. A violation carries a stable `rule` id such as `task.browser.url.scheme`, the `path` it was
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@xema/omni-protocol",
3
- "version": "0.1.20",
3
+ "version": "0.1.21",
4
4
  "description": "The Omni protocol: the contract every provider adapter implements",
5
5
  "type": "module",
6
6
  "license": "MIT",