@inhuman.tools/plexus-expectations 0.0.0-stage → 0.2.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.
Files changed (128) hide show
  1. package/LICENSE.md +7 -0
  2. package/README.md +144 -2
  3. package/dist/driver/ExpectationActor.d.ts +56 -0
  4. package/dist/driver/ExpectationActor.d.ts.map +1 -0
  5. package/dist/driver/ExpectationActor.js +210 -0
  6. package/dist/driver/ExpectationActor.js.map +1 -0
  7. package/dist/driver/PEWDriver.d.ts +144 -0
  8. package/dist/driver/PEWDriver.d.ts.map +1 -0
  9. package/dist/driver/PEWDriver.js +303 -0
  10. package/dist/driver/PEWDriver.js.map +1 -0
  11. package/dist/driver/actor-machine.d.ts +28 -0
  12. package/dist/driver/actor-machine.d.ts.map +1 -0
  13. package/dist/driver/actor-machine.js +50 -0
  14. package/dist/driver/actor-machine.js.map +1 -0
  15. package/dist/driver/connection-miss.d.ts +9 -0
  16. package/dist/driver/connection-miss.d.ts.map +1 -0
  17. package/dist/driver/connection-miss.js +13 -0
  18. package/dist/driver/connection-miss.js.map +1 -0
  19. package/dist/driver/index.d.ts +6 -0
  20. package/dist/driver/index.d.ts.map +1 -0
  21. package/dist/driver/index.js +5 -0
  22. package/dist/driver/index.js.map +1 -0
  23. package/dist/executor/index.d.ts +4 -0
  24. package/dist/executor/index.d.ts.map +1 -0
  25. package/dist/executor/index.js +4 -0
  26. package/dist/executor/index.js.map +1 -0
  27. package/dist/executor/kernel-intern.d.ts +16 -0
  28. package/dist/executor/kernel-intern.d.ts.map +1 -0
  29. package/dist/executor/kernel-intern.js +98 -0
  30. package/dist/executor/kernel-intern.js.map +1 -0
  31. package/dist/executor/orchestrator.d.ts +97 -0
  32. package/dist/executor/orchestrator.d.ts.map +1 -0
  33. package/dist/executor/orchestrator.js +465 -0
  34. package/dist/executor/orchestrator.js.map +1 -0
  35. package/dist/executor/types.d.ts +8 -0
  36. package/dist/executor/types.d.ts.map +1 -0
  37. package/dist/executor/types.js +2 -0
  38. package/dist/executor/types.js.map +1 -0
  39. package/dist/index.d.ts +2 -0
  40. package/dist/index.d.ts.map +1 -0
  41. package/dist/index.js +2 -0
  42. package/dist/index.js.map +1 -0
  43. package/dist/schemas/connection.d.ts +4 -0
  44. package/dist/schemas/connection.d.ts.map +1 -0
  45. package/dist/schemas/connection.js +4 -0
  46. package/dist/schemas/connection.js.map +1 -0
  47. package/dist/schemas/index.d.ts +2 -0
  48. package/dist/schemas/index.d.ts.map +1 -0
  49. package/dist/schemas/index.js +2 -0
  50. package/dist/schemas/index.js.map +1 -0
  51. package/dist/shared/control.d.ts +4 -0
  52. package/dist/shared/control.d.ts.map +1 -0
  53. package/dist/shared/control.js +2 -0
  54. package/dist/shared/control.js.map +1 -0
  55. package/dist/shared/index.d.ts +7 -0
  56. package/dist/shared/index.d.ts.map +1 -0
  57. package/dist/shared/index.js +7 -0
  58. package/dist/shared/index.js.map +1 -0
  59. package/dist/shared/map-field-awareness.d.ts +19 -0
  60. package/dist/shared/map-field-awareness.d.ts.map +1 -0
  61. package/dist/shared/map-field-awareness.js +58 -0
  62. package/dist/shared/map-field-awareness.js.map +1 -0
  63. package/dist/shared/models/ConnectionSpec.d.ts +44 -0
  64. package/dist/shared/models/ConnectionSpec.d.ts.map +1 -0
  65. package/dist/shared/models/ConnectionSpec.js +180 -0
  66. package/dist/shared/models/ConnectionSpec.js.map +1 -0
  67. package/dist/shared/models/DriverSpec.d.ts +45 -0
  68. package/dist/shared/models/DriverSpec.d.ts.map +1 -0
  69. package/dist/shared/models/DriverSpec.js +221 -0
  70. package/dist/shared/models/DriverSpec.js.map +1 -0
  71. package/dist/shared/models/Job.d.ts +85 -0
  72. package/dist/shared/models/Job.d.ts.map +1 -0
  73. package/dist/shared/models/Job.js +261 -0
  74. package/dist/shared/models/Job.js.map +1 -0
  75. package/dist/shared/models/WorkContainer.d.ts +36 -0
  76. package/dist/shared/models/WorkContainer.d.ts.map +1 -0
  77. package/dist/shared/models/WorkContainer.js +140 -0
  78. package/dist/shared/models/WorkContainer.js.map +1 -0
  79. package/dist/shared/models/WorkExpectation.d.ts +63 -0
  80. package/dist/shared/models/WorkExpectation.d.ts.map +1 -0
  81. package/dist/shared/models/WorkExpectation.js +290 -0
  82. package/dist/shared/models/WorkExpectation.js.map +1 -0
  83. package/dist/shared/models/index.d.ts +7 -0
  84. package/dist/shared/models/index.d.ts.map +1 -0
  85. package/dist/shared/models/index.js +6 -0
  86. package/dist/shared/models/index.js.map +1 -0
  87. package/dist/shared/models/jsonRecord.d.ts +14 -0
  88. package/dist/shared/models/jsonRecord.d.ts.map +1 -0
  89. package/dist/shared/models/jsonRecord.js +46 -0
  90. package/dist/shared/models/jsonRecord.js.map +1 -0
  91. package/dist/shared/revalidation-demand.d.ts +27 -0
  92. package/dist/shared/revalidation-demand.d.ts.map +1 -0
  93. package/dist/shared/revalidation-demand.js +116 -0
  94. package/dist/shared/revalidation-demand.js.map +1 -0
  95. package/dist/shared/role.d.ts +12 -0
  96. package/dist/shared/role.d.ts.map +1 -0
  97. package/dist/shared/role.js +21 -0
  98. package/dist/shared/role.js.map +1 -0
  99. package/dist/shared/types.d.ts +43 -0
  100. package/dist/shared/types.d.ts.map +1 -0
  101. package/dist/shared/types.js +12 -0
  102. package/dist/shared/types.js.map +1 -0
  103. package/package.json +79 -3
  104. package/src/driver/ExpectationActor.ts +244 -0
  105. package/src/driver/PEWDriver.ts +302 -0
  106. package/src/driver/actor-machine.ts +54 -0
  107. package/src/driver/connection-miss.ts +13 -0
  108. package/src/driver/index.ts +5 -0
  109. package/src/executor/index.ts +3 -0
  110. package/src/executor/kernel-intern.ts +121 -0
  111. package/src/executor/orchestrator.ts +463 -0
  112. package/src/executor/types.ts +3 -0
  113. package/src/index.ts +1 -0
  114. package/src/schemas/connection.ts +4 -0
  115. package/src/schemas/index.ts +1 -0
  116. package/src/shared/control.ts +4 -0
  117. package/src/shared/index.ts +6 -0
  118. package/src/shared/map-field-awareness.ts +65 -0
  119. package/src/shared/models/ConnectionSpec.ts +84 -0
  120. package/src/shared/models/DriverSpec.ts +102 -0
  121. package/src/shared/models/Job.ts +192 -0
  122. package/src/shared/models/WorkContainer.ts +90 -0
  123. package/src/shared/models/WorkExpectation.ts +229 -0
  124. package/src/shared/models/index.ts +6 -0
  125. package/src/shared/models/jsonRecord.ts +49 -0
  126. package/src/shared/revalidation-demand.ts +121 -0
  127. package/src/shared/role.ts +32 -0
  128. package/src/shared/types.ts +57 -0
@@ -0,0 +1,6 @@
1
+ export * from "./WorkContainer.js";
2
+ export * from "./WorkExpectation.js";
3
+ export * from "./Job.js";
4
+ export * from "./DriverSpec.js";
5
+ export * from "./ConnectionSpec.js";
6
+ export { type LiveFacts as LiveFacts } from "./jsonRecord.js";
@@ -0,0 +1,49 @@
1
+ /**
2
+ * Plexus `@syncing.record` bags are `Record<string, string>`. Values round-trip
3
+ * through JSON.stringify; `undefined` is omitted.
4
+ */
5
+
6
+ /** Process self-report on ConnectionSpec / DriverSpec. JSON through `liveMap`. */
7
+ export type LiveFacts = {
8
+ readonly [key: string]: unknown | undefined;
9
+ readonly models?: readonly string[];
10
+ readonly tools?: unknown;
11
+ };
12
+ export function readJsonRecord(map: Record<string, string>): Record<string, unknown> {
13
+ const out: Record<string, unknown> = {};
14
+ for (const [key, raw] of Object.entries(map)) {
15
+ try {
16
+ out[key] = JSON.parse(raw);
17
+ } catch {
18
+ out[key] = raw;
19
+ }
20
+ }
21
+ return deepFreeze(out);
22
+ }
23
+
24
+ export function writeJsonRecord(
25
+ map: Record<string, string>,
26
+ next: Readonly<Record<string, unknown>> | undefined,
27
+ ): void {
28
+ const encoded: Record<string, string> = {};
29
+ for (const [key, value] of Object.entries(next ?? {})) {
30
+ if (value === undefined) continue;
31
+ encoded[key] = JSON.stringify(value);
32
+ }
33
+ for (const key of Object.keys(map)) {
34
+ if (!(key in encoded)) delete map[key];
35
+ }
36
+ for (const [key, value] of Object.entries(encoded)) {
37
+ if (map[key] !== value) map[key] = value;
38
+ }
39
+ }
40
+
41
+ export function deepFreeze<T>(value: T): T {
42
+ if (value === null || typeof value !== "object" || Object.isFrozen(value)) return value;
43
+ if (Array.isArray(value)) {
44
+ for (const item of value) deepFreeze(item);
45
+ } else {
46
+ for (const item of Object.values(value as Record<string, unknown>)) deepFreeze(item);
47
+ }
48
+ return Object.freeze(value);
49
+ }
@@ -0,0 +1,121 @@
1
+ /**
2
+ * Revalidation demand. Clients publish on kernel awareness; PEW reduces to
3
+ * none | secondary | primary. Interned connections outlive a session wrap, so
4
+ * occupancy is not session-plane. Drivers read the reduced token — never clientIds.
5
+ */
6
+ import { FieldAwareness, type PlexusAwareness } from "@here.build/plexus";
7
+
8
+ import { ConnectionSpec } from "./models/ConnectionSpec.js";
9
+ import { DriverSpec } from "./models/DriverSpec.js";
10
+
11
+ export type RevalidationDemand = "none" | "secondary" | "primary";
12
+
13
+ export const REVALIDATION_DEMAND_LANE = "revalidationDemand";
14
+ export const REVALIDATION_DEMAND_FOCUS_LANE = "revalidationDemandFocus";
15
+
16
+ export type RevalidationDemandRef =
17
+ | { readonly kind: "driver"; readonly name: string }
18
+ | { readonly kind: "connection"; readonly id: string };
19
+
20
+ export type RevalidationDemandSubject = DriverSpec | ConnectionSpec;
21
+
22
+ export type RevalidationDemandSource = RevalidationDemandSubject | Iterable<RevalidationDemandSubject | null | undefined> | null | undefined;
23
+
24
+ export function revalidationDemandRefFromSpec(spec: RevalidationDemandSubject): RevalidationDemandRef {
25
+ if (isConnectionSubject(spec)) return { kind: "connection", id: spec.id };
26
+ return { kind: "driver", name: spec.name };
27
+ }
28
+
29
+ export function collectRevalidationDemandRefs(source: RevalidationDemandSource): RevalidationDemandRef[] {
30
+ if (source == null) return [];
31
+ if (isRevalidationDemandSubject(source)) return [revalidationDemandRefFromSpec(source)];
32
+ const out: RevalidationDemandRef[] = [];
33
+ const seen = new Set<string>();
34
+ for (const item of source) {
35
+ if (item == null || !isRevalidationDemandSubject(item)) continue;
36
+ const ref = revalidationDemandRefFromSpec(item);
37
+ const key = refKey(ref);
38
+ if (seen.has(key)) continue;
39
+ seen.add(key);
40
+ out.push(ref);
41
+ }
42
+ return out;
43
+ }
44
+
45
+ export function publishRevalidationDemand(hub: PlexusAwareness, refs: readonly RevalidationDemandRef[]): void {
46
+ const lane = new FieldAwareness(hub, REVALIDATION_DEMAND_LANE);
47
+ if (refs.length === 0) {
48
+ lane.clear();
49
+ return;
50
+ }
51
+ lane.set(refs as never);
52
+ }
53
+
54
+ export function publishRevalidationDemandFocus(hub: PlexusAwareness, ref: RevalidationDemandRef | null): void {
55
+ const lane = new FieldAwareness(hub, REVALIDATION_DEMAND_FOCUS_LANE);
56
+ if (ref == null) {
57
+ lane.clear();
58
+ return;
59
+ }
60
+ lane.set(ref as never);
61
+ }
62
+
63
+ /** Union of every client's occupancy. Primary wins over secondary. */
64
+ export function revalidationDemandOf(hub: PlexusAwareness, target: RevalidationDemandRef): RevalidationDemand {
65
+ const focus = new FieldAwareness(hub, REVALIDATION_DEMAND_FOCUS_LANE);
66
+ for (const clientId of focus.clientIds) {
67
+ const value = asRevalidationDemandRef(focus.getOther(clientId));
68
+ if (value != null && sameRef(value, target)) return "primary";
69
+ }
70
+ const ambient = new FieldAwareness(hub, REVALIDATION_DEMAND_LANE);
71
+ for (const clientId of ambient.clientIds) {
72
+ const list = asRevalidationDemandRefList(ambient.getOther(clientId));
73
+ if (list.some((item) => sameRef(item, target))) return "secondary";
74
+ }
75
+ return "none";
76
+ }
77
+
78
+ function isConnectionSubject(spec: RevalidationDemandSubject): spec is ConnectionSpec {
79
+ if (spec instanceof ConnectionSpec) return true;
80
+ if (spec instanceof DriverSpec) return false;
81
+ return "driver" in spec && "configuration" in spec;
82
+ }
83
+
84
+ function isRevalidationDemandSubject(value: unknown): value is RevalidationDemandSubject {
85
+ if (value instanceof ConnectionSpec || value instanceof DriverSpec) return true;
86
+ if (typeof value !== "object" || value === null) return false;
87
+ if ("driver" in value && "configuration" in value) return true;
88
+ return "importPath" in value && "abilityClaim" in value;
89
+ }
90
+
91
+ function asRevalidationDemandRef(value: unknown): RevalidationDemandRef | null {
92
+ if (typeof value !== "object" || value === null) return null;
93
+ const rec = value as { kind?: unknown; name?: unknown; id?: unknown };
94
+ if (rec.kind === "driver" && typeof rec.name === "string" && rec.name !== "") {
95
+ return { kind: "driver", name: rec.name };
96
+ }
97
+ if (rec.kind === "connection" && typeof rec.id === "string" && rec.id !== "") {
98
+ return { kind: "connection", id: rec.id };
99
+ }
100
+ return null;
101
+ }
102
+
103
+ function asRevalidationDemandRefList(value: unknown): RevalidationDemandRef[] {
104
+ if (!Array.isArray(value)) return [];
105
+ const out: RevalidationDemandRef[] = [];
106
+ for (const item of value) {
107
+ const ref = asRevalidationDemandRef(item);
108
+ if (ref != null) out.push(ref);
109
+ }
110
+ return out;
111
+ }
112
+
113
+ function sameRef(a: RevalidationDemandRef, b: RevalidationDemandRef): boolean {
114
+ if (a.kind === "driver" && b.kind === "driver") return a.name === b.name;
115
+ if (a.kind === "connection" && b.kind === "connection") return a.id === b.id;
116
+ return false;
117
+ }
118
+
119
+ function refKey(ref: RevalidationDemandRef): string {
120
+ return ref.kind === "driver" ? `driver:${ref.name}` : `connection:${ref.id}`;
121
+ }
@@ -0,0 +1,32 @@
1
+ /**
2
+ * Role occupancy. One awareness lane, keyed by expectation uuid.
3
+ * Schema is append-only (`PlexusAwareness.MAX_LANES` = 64); a lane per
4
+ * expectation exhausts the budget. Retract the uuid on leave — occupancy
5
+ * is presence, not a durable outcome. `""` occupies without a report frame.
6
+ */
7
+ import { type PlexusAwareness } from "@here.build/plexus";
8
+
9
+ import { MapFieldAwareness } from "./map-field-awareness.js";
10
+ import { ROLE_EXPECTATIONS_LANE } from "./types.js";
11
+
12
+ export function reportRole(hub: PlexusAwareness, uuid: string, value: unknown): void {
13
+ const field = lane(hub);
14
+ if (value === null) {
15
+ field.mapDelete(uuid);
16
+ return;
17
+ }
18
+ field.mapSet(uuid, value);
19
+ }
20
+
21
+ export function retractRole(hub: PlexusAwareness, uuid: string): void {
22
+ lane(hub).mapDelete(uuid);
23
+ }
24
+
25
+ /** First value for `uuid` across bases. Missing key is `undefined`. */
26
+ export function roleOf(hub: PlexusAwareness, uuid: string): unknown {
27
+ return lane(hub).mapGet(uuid);
28
+ }
29
+
30
+ function lane(hub: PlexusAwareness): MapFieldAwareness {
31
+ return new MapFieldAwareness(hub, ROLE_EXPECTATIONS_LANE);
32
+ }
@@ -0,0 +1,57 @@
1
+ import type { AwarenessSerializable, AwarenessShape, Plexus, PlexusModel } from "@here.build/plexus";
2
+
3
+ import type { ConnectionSpec } from "./models/ConnectionSpec.js";
4
+ import type { DriverSpec } from "./models/DriverSpec.js";
5
+ import type { WorkContainer } from "./models/WorkContainer.js";
6
+ import type { RevalidationDemandRef } from "./revalidation-demand.js";
7
+
8
+ /** `null` = never ensured / never loaded. */
9
+ export type ReadyState = null | "bootstrapping" | "ready" | "failed";
10
+
11
+ export function asReadyState(value: unknown): ReadyState {
12
+ if (value === "bootstrapping" || value === "ready" || value === "failed") return value;
13
+ return null;
14
+ }
15
+
16
+ /**
17
+ * Revalidation demand is kernel-plane: interned connections outlive a session wrap.
18
+ * Live catalog faces stay on DriverSpec / ConnectionSpec, not awareness lanes.
19
+ */
20
+ export type KernelAwareness = AwarenessShape & {
21
+ revalidationDemand?: readonly RevalidationDemandRef[];
22
+ revalidationDemandFocus?: RevalidationDemandRef | null;
23
+ };
24
+
25
+ /** Live shell tails: one lane, `Record<job.uuid, frame>`. */
26
+ export const SHELL_TAILS_LANE = "shell-tails";
27
+
28
+ /** Role occupancy: one lane, `Record<expectation.uuid, frame | "">`. */
29
+ export const ROLE_EXPECTATIONS_LANE = "role:expectations";
30
+
31
+ /** Statusline left slot: the daemon's last successful eval. */
32
+ export const STATUSLINE_LANE = "statusline";
33
+
34
+ export type SessionAwareness = AwarenessShape & {
35
+ /** Operator `policy.json` load/eval throw. Presence, not a leaf vote. */
36
+ policyError?: string;
37
+ [STATUSLINE_LANE]?: string;
38
+ [SHELL_TAILS_LANE]?: Record<string, AwarenessSerializable>;
39
+ [ROLE_EXPECTATIONS_LANE]?: Record<string, AwarenessSerializable>;
40
+ };
41
+
42
+ /**
43
+ * Catalog parent of {@link DriverSpec} / {@link ConnectionSpec}.
44
+ * Ordinary records, not virtual maps.
45
+ */
46
+ export type SpecCatalog = PlexusModel<null> & {
47
+ readonly drivers: Record<string, DriverSpec>;
48
+ readonly connections: Record<string, ConnectionSpec>;
49
+ };
50
+
51
+ /** Product membership is YAML projected onto these records. */
52
+ export type KernelRootModel = SpecCatalog;
53
+
54
+ export type SessionRootModel = WorkContainer;
55
+
56
+ export type KernelPlexus = Plexus<KernelRootModel, KernelAwareness>;
57
+ export type SessionPlexus = Plexus<SessionRootModel, SessionAwareness>;