@you-agent-factory/factory-replay 0.0.0 → 0.0.2
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/LICENSE.md +1 -1
- package/README.md +88 -56
- package/dist/activity-contract.d.ts +62 -0
- package/dist/activity-contract.js +1 -0
- package/dist/activity-replay.d.ts +3 -0
- package/dist/activity-replay.js +165 -0
- package/dist/activity.d.ts +7 -0
- package/dist/activity.js +306 -0
- package/dist/index.d.ts +9 -0
- package/dist/index.js +7 -0
- package/dist/load-contract.d.ts +57 -0
- package/dist/load-contract.js +1 -0
- package/dist/load-replay.d.ts +3 -0
- package/dist/load-replay.js +156 -0
- package/dist/load.d.ts +3 -0
- package/dist/load.js +260 -0
- package/dist/progress.d.ts +44 -0
- package/dist/progress.js +278 -0
- package/dist/replay.d.ts +98 -0
- package/dist/replay.js +144 -0
- package/dist/topology-connection.d.ts +3 -0
- package/dist/topology-connection.js +80 -0
- package/dist/topology-contract.d.ts +187 -0
- package/dist/topology-contract.js +67 -0
- package/dist/topology-identity.d.ts +5 -0
- package/dist/topology-identity.js +8 -0
- package/dist/topology.d.ts +9 -0
- package/dist/topology.js +255 -0
- package/package.json +36 -11
- package/src/activity.js +0 -208
- package/src/index.d.ts +0 -324
- package/src/index.js +0 -477
- package/src/progress.js +0 -117
- package/src/topology.js +0 -398
package/dist/activity.js
ADDED
|
@@ -0,0 +1,306 @@
|
|
|
1
|
+
import { projectFactoryLoad } from "./load.js";
|
|
2
|
+
import { projectFactoryTopology } from "./topology.js";
|
|
3
|
+
import { factoryTopologyEntityId, factoryTopologyNodeId, } from "./topology-identity.js";
|
|
4
|
+
function sortedByIdentity(values) {
|
|
5
|
+
return [...(values ?? [])].sort((left, right) => {
|
|
6
|
+
const identityDifference = factoryTopologyEntityId(left.id, left.name).localeCompare(factoryTopologyEntityId(right.id, right.name));
|
|
7
|
+
return identityDifference || left.name.localeCompare(right.name);
|
|
8
|
+
});
|
|
9
|
+
}
|
|
10
|
+
/** Build the stable public identity of one active Dispatch overlay. */
|
|
11
|
+
export function factoryDispatchOverlayId(dispatchId) {
|
|
12
|
+
return `dispatch:${dispatchId}`;
|
|
13
|
+
}
|
|
14
|
+
/** Build the stable public identity used when an overlay references Work. */
|
|
15
|
+
export function factoryWorkProjectionId(workId) {
|
|
16
|
+
return `work:${workId}`;
|
|
17
|
+
}
|
|
18
|
+
/** Project selected-tick active Dispatch overlays and their topology activity. */
|
|
19
|
+
export function projectFactoryActivity(input) {
|
|
20
|
+
const issues = new Map();
|
|
21
|
+
const index = indexFactory(input.factory, input.selectedTick);
|
|
22
|
+
const dispatches = normalizeDispatches(input.activeDispatches, issues);
|
|
23
|
+
const activeDispatchOverlays = dispatches.map((dispatch) => projectOverlay(dispatch, index, issues));
|
|
24
|
+
const load = projectFactoryLoad({
|
|
25
|
+
activeDispatches: dispatches.map((dispatch) => ({
|
|
26
|
+
id: dispatch.id,
|
|
27
|
+
...(dispatch.resourceNames
|
|
28
|
+
? {
|
|
29
|
+
resourceClaims: dispatch.resourceNames.map((resourceName) => ({
|
|
30
|
+
resourceName,
|
|
31
|
+
})),
|
|
32
|
+
}
|
|
33
|
+
: {}),
|
|
34
|
+
})),
|
|
35
|
+
factory: input.factory,
|
|
36
|
+
selectedTick: input.selectedTick,
|
|
37
|
+
works: [],
|
|
38
|
+
});
|
|
39
|
+
appendLoadIssues(load.issues, issues);
|
|
40
|
+
return {
|
|
41
|
+
activeDispatchOverlays: activeDispatchOverlays.sort((left, right) => left.id.localeCompare(right.id)),
|
|
42
|
+
activeWorkstationNodeIds: [
|
|
43
|
+
...new Set(activeDispatchOverlays.flatMap((overlay) => overlay.workstationNodeId ? [overlay.workstationNodeId] : [])),
|
|
44
|
+
].sort(),
|
|
45
|
+
issues: [...issues.values()].sort((left, right) => left.id.localeCompare(right.id)),
|
|
46
|
+
resourceOccupancy: load.resourceOccupancy,
|
|
47
|
+
selectedTick: input.selectedTick,
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
function indexFactory(factory, selectedTick) {
|
|
51
|
+
const topology = projectFactoryTopology({ factory, selectedTick });
|
|
52
|
+
const resourcesByName = new Map();
|
|
53
|
+
const workersByName = new Map();
|
|
54
|
+
const workstationsByTransition = new Map();
|
|
55
|
+
const workStates = new Map();
|
|
56
|
+
for (const resource of sortedByIdentity(factory?.resources)) {
|
|
57
|
+
const id = factoryTopologyEntityId(resource.id, resource.name);
|
|
58
|
+
resourcesByName.set(resource.name, {
|
|
59
|
+
id,
|
|
60
|
+
nodeId: factoryTopologyNodeId("resource", id),
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
for (const worker of sortedByIdentity(factory?.workers)) {
|
|
64
|
+
const id = factoryTopologyEntityId(worker.id, worker.name);
|
|
65
|
+
const value = { id, nodeId: factoryTopologyNodeId("worker", id) };
|
|
66
|
+
workersByName.set(worker.name, value);
|
|
67
|
+
workersByName.set(id, value);
|
|
68
|
+
}
|
|
69
|
+
for (const workType of sortedByIdentity(factory?.workTypes)) {
|
|
70
|
+
const workTypeId = factoryTopologyEntityId(workType.id, workType.name);
|
|
71
|
+
for (const state of sortedByIdentity(workType.states)) {
|
|
72
|
+
const stateId = `${workTypeId}:${factoryTopologyEntityId(state.id, state.name)}`;
|
|
73
|
+
const nodeId = factoryTopologyNodeId("work-state", stateId);
|
|
74
|
+
for (const typeReference of [workType.name, workTypeId]) {
|
|
75
|
+
for (const stateReference of [state.name, state.id?.trim()]) {
|
|
76
|
+
if (stateReference) {
|
|
77
|
+
workStates.set(`${typeReference}\u0000${stateReference}`, nodeId);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
for (const workstation of sortedByIdentity(factory?.workstations)) {
|
|
84
|
+
const id = factoryTopologyEntityId(workstation.id, workstation.name);
|
|
85
|
+
const value = {
|
|
86
|
+
id,
|
|
87
|
+
nodeId: factoryTopologyNodeId("workstation", id),
|
|
88
|
+
workstation,
|
|
89
|
+
};
|
|
90
|
+
workstationsByTransition.set(workstation.name, value);
|
|
91
|
+
workstationsByTransition.set(id, value);
|
|
92
|
+
}
|
|
93
|
+
return {
|
|
94
|
+
resourcesByName,
|
|
95
|
+
topology,
|
|
96
|
+
workersByName,
|
|
97
|
+
workstationsByTransition,
|
|
98
|
+
workStates,
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
function projectOverlay(dispatch, index, issues) {
|
|
102
|
+
const resolved = dispatch.transitionId
|
|
103
|
+
? index.workstationsByTransition.get(dispatch.transitionId)
|
|
104
|
+
: undefined;
|
|
105
|
+
if (!resolved && dispatch.transitionId) {
|
|
106
|
+
addIssue(issues, {
|
|
107
|
+
code: "UNRESOLVED_WORKSTATION",
|
|
108
|
+
dispatchId: dispatch.id,
|
|
109
|
+
id: `dispatch:${dispatch.id}:workstation:${dispatch.transitionId}`,
|
|
110
|
+
message: `Dispatch ${dispatch.id} refers to unknown workstation transition ${dispatch.transitionId}.`,
|
|
111
|
+
reference: dispatch.transitionId,
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
const workerName = resolved?.workstation.worker?.trim();
|
|
115
|
+
const worker = workerName ? index.workersByName.get(workerName) : undefined;
|
|
116
|
+
if (workerName && !worker) {
|
|
117
|
+
addIssue(issues, {
|
|
118
|
+
code: "UNRESOLVED_WORKER",
|
|
119
|
+
dispatchId: dispatch.id,
|
|
120
|
+
id: `dispatch:${dispatch.id}:worker:${workerName}`,
|
|
121
|
+
message: `Dispatch ${dispatch.id} refers to unknown worker ${workerName}.`,
|
|
122
|
+
reference: workerName,
|
|
123
|
+
});
|
|
124
|
+
}
|
|
125
|
+
const resources = resolveResources(dispatch, index, issues);
|
|
126
|
+
const routeNodeIds = resolveRouteNodeIds(dispatch, index, issues);
|
|
127
|
+
const connectionIds = resolveConnections(dispatch, index.topology, resolved?.nodeId, worker?.nodeId, resources?.map((resource) => resource.nodeId), routeNodeIds, issues);
|
|
128
|
+
const workIds = dispatch.workIds
|
|
129
|
+
? [...new Set(dispatch.workIds)].sort()
|
|
130
|
+
: undefined;
|
|
131
|
+
return {
|
|
132
|
+
connectionIds,
|
|
133
|
+
dispatchId: dispatch.id,
|
|
134
|
+
evidence: {
|
|
135
|
+
resources: dispatch.resourceNames ? "known" : "unavailable",
|
|
136
|
+
route: dispatch.inputRoutes ? "known" : "unavailable",
|
|
137
|
+
work: dispatch.workIds ? "known" : "unavailable",
|
|
138
|
+
worker: worker ? "known" : "unavailable",
|
|
139
|
+
workstation: resolved ? "known" : "unavailable",
|
|
140
|
+
},
|
|
141
|
+
id: factoryDispatchOverlayId(dispatch.id),
|
|
142
|
+
...(resources
|
|
143
|
+
? {
|
|
144
|
+
resourceIds: resources.map((resource) => resource.id).sort(),
|
|
145
|
+
resourceNodeIds: resources.map((resource) => resource.nodeId).sort(),
|
|
146
|
+
}
|
|
147
|
+
: {}),
|
|
148
|
+
startedTick: dispatch.startedTick,
|
|
149
|
+
...(dispatch.transitionId ? { transitionId: dispatch.transitionId } : {}),
|
|
150
|
+
...(worker ? { workerId: worker.id, workerNodeId: worker.nodeId } : {}),
|
|
151
|
+
...(workIds
|
|
152
|
+
? {
|
|
153
|
+
workIds,
|
|
154
|
+
workProjectionIds: workIds.map(factoryWorkProjectionId),
|
|
155
|
+
}
|
|
156
|
+
: {}),
|
|
157
|
+
...(resolved
|
|
158
|
+
? {
|
|
159
|
+
workstationId: resolved.id,
|
|
160
|
+
workstationNodeId: resolved.nodeId,
|
|
161
|
+
}
|
|
162
|
+
: {}),
|
|
163
|
+
};
|
|
164
|
+
}
|
|
165
|
+
function resolveResources(dispatch, index, issues) {
|
|
166
|
+
if (!dispatch.resourceNames)
|
|
167
|
+
return undefined;
|
|
168
|
+
const resources = new Map();
|
|
169
|
+
for (const resourceName of dispatch.resourceNames) {
|
|
170
|
+
const resource = index.resourcesByName.get(resourceName);
|
|
171
|
+
if (resource) {
|
|
172
|
+
resources.set(resource.id, resource);
|
|
173
|
+
continue;
|
|
174
|
+
}
|
|
175
|
+
addIssue(issues, {
|
|
176
|
+
code: "UNRESOLVED_RESOURCE",
|
|
177
|
+
dispatchId: dispatch.id,
|
|
178
|
+
id: `dispatch:${dispatch.id}:resource:${resourceName}`,
|
|
179
|
+
message: `Dispatch ${dispatch.id} refers to unknown resource ${resourceName}.`,
|
|
180
|
+
reference: resourceName,
|
|
181
|
+
});
|
|
182
|
+
}
|
|
183
|
+
return [...resources.values()];
|
|
184
|
+
}
|
|
185
|
+
function resolveRouteNodeIds(dispatch, index, issues) {
|
|
186
|
+
if (!dispatch.inputRoutes)
|
|
187
|
+
return undefined;
|
|
188
|
+
const nodeIds = new Set();
|
|
189
|
+
for (const route of dispatch.inputRoutes) {
|
|
190
|
+
const nodeId = resolveRouteNodeId(route, index);
|
|
191
|
+
if (nodeId) {
|
|
192
|
+
nodeIds.add(nodeId);
|
|
193
|
+
continue;
|
|
194
|
+
}
|
|
195
|
+
const reference = `${route.workTypeId ?? "?"}:${route.stateId ?? route.stateName ?? "?"}`;
|
|
196
|
+
addIssue(issues, {
|
|
197
|
+
code: "UNRESOLVED_ROUTE",
|
|
198
|
+
dispatchId: dispatch.id,
|
|
199
|
+
id: `dispatch:${dispatch.id}:route:${reference}`,
|
|
200
|
+
message: `Dispatch ${dispatch.id} input route ${reference} is not present in the selected topology.`,
|
|
201
|
+
reference,
|
|
202
|
+
});
|
|
203
|
+
}
|
|
204
|
+
return [...nodeIds].sort();
|
|
205
|
+
}
|
|
206
|
+
function resolveRouteNodeId(route, index) {
|
|
207
|
+
if (!route.workTypeId)
|
|
208
|
+
return undefined;
|
|
209
|
+
for (const stateReference of [route.stateId, route.stateName]) {
|
|
210
|
+
if (!stateReference)
|
|
211
|
+
continue;
|
|
212
|
+
const nodeId = index.workStates.get(`${route.workTypeId}\u0000${stateReference}`);
|
|
213
|
+
if (nodeId)
|
|
214
|
+
return nodeId;
|
|
215
|
+
}
|
|
216
|
+
return undefined;
|
|
217
|
+
}
|
|
218
|
+
function resolveConnections(dispatch, topology, workstationNodeId, workerNodeId, resourceNodeIds, routeNodeIds, issues) {
|
|
219
|
+
if (!workstationNodeId)
|
|
220
|
+
return [];
|
|
221
|
+
if (!topology.ok) {
|
|
222
|
+
addIssue(issues, {
|
|
223
|
+
code: "UNAVAILABLE_TOPOLOGY_PATH",
|
|
224
|
+
dispatchId: dispatch.id,
|
|
225
|
+
id: `dispatch:${dispatch.id}:topology-path-unavailable`,
|
|
226
|
+
message: `Dispatch ${dispatch.id} topology paths are unavailable because the selected topology is invalid.`,
|
|
227
|
+
});
|
|
228
|
+
return [];
|
|
229
|
+
}
|
|
230
|
+
const resourceSet = resourceNodeIds && new Set(resourceNodeIds);
|
|
231
|
+
const routeSet = routeNodeIds && new Set(routeNodeIds);
|
|
232
|
+
return topology.connections
|
|
233
|
+
.filter((connection) => isRelevantConnection(connection, workstationNodeId, workerNodeId, resourceSet, routeSet))
|
|
234
|
+
.map((connection) => connection.id)
|
|
235
|
+
.sort();
|
|
236
|
+
}
|
|
237
|
+
function isRelevantConnection(connection, workstationNodeId, workerNodeId, resourceNodeIds, routeNodeIds) {
|
|
238
|
+
if (connection.kind === "worker-assignment" &&
|
|
239
|
+
workerNodeId &&
|
|
240
|
+
connection.source.nodeId === workerNodeId &&
|
|
241
|
+
connection.target.nodeId === workstationNodeId) {
|
|
242
|
+
return true;
|
|
243
|
+
}
|
|
244
|
+
if (connection.kind === "workstation-resource" &&
|
|
245
|
+
resourceNodeIds?.has(connection.source.nodeId) &&
|
|
246
|
+
connection.target.nodeId === workstationNodeId) {
|
|
247
|
+
return true;
|
|
248
|
+
}
|
|
249
|
+
return Boolean(connection.kind === "workstation-input" &&
|
|
250
|
+
routeNodeIds?.has(connection.source.nodeId) &&
|
|
251
|
+
connection.target.nodeId === workstationNodeId);
|
|
252
|
+
}
|
|
253
|
+
function normalizeDispatches(dispatches, issues) {
|
|
254
|
+
const grouped = new Map();
|
|
255
|
+
for (const dispatch of dispatches) {
|
|
256
|
+
const values = grouped.get(dispatch.id) ?? [];
|
|
257
|
+
values.push(dispatch);
|
|
258
|
+
grouped.set(dispatch.id, values);
|
|
259
|
+
}
|
|
260
|
+
const normalized = [];
|
|
261
|
+
for (const [dispatchId, values] of grouped) {
|
|
262
|
+
const keyed = new Map(values.map((value) => [evidenceKey(value), value]));
|
|
263
|
+
if (keyed.size > 1) {
|
|
264
|
+
addIssue(issues, {
|
|
265
|
+
code: "CONTRADICTORY_DISPATCH_EVIDENCE",
|
|
266
|
+
dispatchId,
|
|
267
|
+
id: `dispatch:${dispatchId}:contradictory-evidence`,
|
|
268
|
+
message: `Dispatch ${dispatchId} has contradictory selected-tick activity evidence.`,
|
|
269
|
+
});
|
|
270
|
+
}
|
|
271
|
+
normalized.push([...keyed.values()].sort((left, right) => evidenceKey(left).localeCompare(evidenceKey(right)))[0]);
|
|
272
|
+
}
|
|
273
|
+
return normalized.sort((left, right) => left.id.localeCompare(right.id));
|
|
274
|
+
}
|
|
275
|
+
function evidenceKey(dispatch) {
|
|
276
|
+
const sorted = (values) => values ? [...values].sort().join("\u0000") : "?";
|
|
277
|
+
return [
|
|
278
|
+
dispatch.startedTick,
|
|
279
|
+
dispatch.transitionId ?? "?",
|
|
280
|
+
sorted(dispatch.workIds),
|
|
281
|
+
sorted(dispatch.resourceNames),
|
|
282
|
+
dispatch.inputRoutes
|
|
283
|
+
? dispatch.inputRoutes
|
|
284
|
+
.map((route) => `${route.workTypeId ?? "?"}\u0000${route.stateId ?? "?"}\u0000${route.stateName ?? "?"}`)
|
|
285
|
+
.sort()
|
|
286
|
+
.join("\u0002")
|
|
287
|
+
: "?",
|
|
288
|
+
].join("\u0001");
|
|
289
|
+
}
|
|
290
|
+
function appendLoadIssues(loadIssues, issues) {
|
|
291
|
+
for (const issue of loadIssues) {
|
|
292
|
+
const code = issue.code === "UNRESOLVED_RESOURCE_CLAIM"
|
|
293
|
+
? "UNRESOLVED_RESOURCE"
|
|
294
|
+
: issue.code;
|
|
295
|
+
if (code !== "INVALID_RESOURCE_CAPACITY" &&
|
|
296
|
+
code !== "MISSING_FACTORY" &&
|
|
297
|
+
code !== "RESOURCE_CAPACITY_EXCEEDED" &&
|
|
298
|
+
code !== "UNRESOLVED_RESOURCE") {
|
|
299
|
+
continue;
|
|
300
|
+
}
|
|
301
|
+
addIssue(issues, { ...issue, code });
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
function addIssue(issues, issue) {
|
|
305
|
+
issues.set(issue.id, issue);
|
|
306
|
+
}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export { factoryDispatchOverlayId, factoryWorkProjectionId, projectFactoryActivity, } from "./activity.js";
|
|
2
|
+
export type { FactoryActiveDispatchEvidence, FactoryActivityAtTickInput, FactoryActivityProjection, FactoryActivityProjectionInput, FactoryActivityProjectionIssue, FactoryDispatchOverlayEvidenceStatus, FactoryDispatchOverlayProjection, FactoryDispatchRouteEvidence, } from "./activity-contract.js";
|
|
3
|
+
export { projectFactoryActivityAtTick } from "./activity-replay.js";
|
|
4
|
+
export { projectFactoryLoad } from "./load.js";
|
|
5
|
+
export type { FactoryActiveResourceClaimsEvidence, FactoryLoadAtTickInput, FactoryLoadProjection, FactoryLoadProjectionInput, FactoryLoadProjectionIssue, FactoryResourceClaimEvidence, FactoryResourceOccupancyProjection, FactoryWorkStateCountProjection, FactoryWorkStateOccupancyEvidence, } from "./load-contract.js";
|
|
6
|
+
export { projectFactoryLoadAtTick } from "./load-replay.js";
|
|
7
|
+
export { type FactoryWorkProgressAtTickInput, type FactoryWorkProgressCategory, type FactoryWorkProgressCounts, type FactoryWorkProgressEvidence, type FactoryWorkProgressItem, type FactoryWorkProgressProjection, type FactoryWorkProgressProjectionInput, type FactoryWorkProgressStateEvidence, type FactoryWorkStateCategory, projectFactoryWorkProgress, projectFactoryWorkProgressAtTick, } from "./progress.js";
|
|
8
|
+
export { advanceFactoryReplay, advanceFactoryReplayCheckpoint, canonicalizeFactoryEvents, createFactoryReplayWorldCheckpoint, type FactoryReplayCheckpoint, type FactoryReplayCheckpointAdvancement, type FactoryReplayFixedSelection, type FactoryReplayInitialization, type FactoryReplayReducer, type FactoryReplayResult, type FactoryReplaySelection, type FactoryReplayStateCloner, type FactoryReplayWorldAdvanceInput, type FactoryReplayWorldAdvanceResult, type FactoryReplayWorldCheckpoint, type FactoryReplayWorldReducer, type FactoryReplayWorldResult, initializeFactoryReplay, projectFactoryStateAtTick, projectFactoryWorldAtTick, } from "./replay.js";
|
|
9
|
+
export { FACTORY_TOPOLOGY_RELATIONSHIPS, type FactoryResourceTopologyNode, type FactoryTopologyAtTickInput, type FactoryTopologyConnection, type FactoryTopologyConnectionCandidate, type FactoryTopologyConnectionEndpoint, type FactoryTopologyConnectionKind, type FactoryTopologyConnectionResult, type FactoryTopologyHandle, type FactoryTopologyHandleId, type FactoryTopologyNode, type FactoryTopologyNodeKind, type FactoryTopologyProjection, type FactoryTopologyProjectionInput, type FactoryTopologyProjectionIssue, type FactoryWorkerTopologyNode, type FactoryWorkStateTopologyNode, type FactoryWorkstationTopologyNode, type FactoryWorkTypeTopologyNode, factoryTopologyEntityId, factoryTopologyNodeId, projectFactoryTopology, projectFactoryTopologyAtTick, projectFactoryTopologyConnection, } from "./topology.js";
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export { factoryDispatchOverlayId, factoryWorkProjectionId, projectFactoryActivity, } from "./activity.js";
|
|
2
|
+
export { projectFactoryActivityAtTick } from "./activity-replay.js";
|
|
3
|
+
export { projectFactoryLoad } from "./load.js";
|
|
4
|
+
export { projectFactoryLoadAtTick } from "./load-replay.js";
|
|
5
|
+
export { projectFactoryWorkProgress, projectFactoryWorkProgressAtTick, } from "./progress.js";
|
|
6
|
+
export { advanceFactoryReplay, advanceFactoryReplayCheckpoint, canonicalizeFactoryEvents, createFactoryReplayWorldCheckpoint, initializeFactoryReplay, projectFactoryStateAtTick, projectFactoryWorldAtTick, } from "./replay.js";
|
|
7
|
+
export { FACTORY_TOPOLOGY_RELATIONSHIPS, factoryTopologyEntityId, factoryTopologyNodeId, projectFactoryTopology, projectFactoryTopologyAtTick, projectFactoryTopologyConnection, } from "./topology.js";
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import type { FactoryDefinition, FactoryEvent } from "@you-agent-factory/client";
|
|
2
|
+
export interface FactoryWorkStateOccupancyEvidence {
|
|
3
|
+
id: string;
|
|
4
|
+
stateId?: string;
|
|
5
|
+
stateName?: string;
|
|
6
|
+
workTypeId?: string;
|
|
7
|
+
}
|
|
8
|
+
export interface FactoryResourceClaimEvidence {
|
|
9
|
+
resourceName: string;
|
|
10
|
+
quantity?: number;
|
|
11
|
+
}
|
|
12
|
+
export interface FactoryActiveResourceClaimsEvidence {
|
|
13
|
+
id: string;
|
|
14
|
+
resourceClaims?: readonly FactoryResourceClaimEvidence[];
|
|
15
|
+
}
|
|
16
|
+
export interface FactoryWorkStateCountProjection {
|
|
17
|
+
count?: number;
|
|
18
|
+
evidence: "known" | "unavailable";
|
|
19
|
+
workIds?: string[];
|
|
20
|
+
workStateId: string;
|
|
21
|
+
workStateNodeId: string;
|
|
22
|
+
workTypeId: string;
|
|
23
|
+
}
|
|
24
|
+
export interface FactoryResourceOccupancyProjection {
|
|
25
|
+
availableQuantity?: number;
|
|
26
|
+
capacity?: number;
|
|
27
|
+
capacityEvidence: "known" | "unavailable";
|
|
28
|
+
evidence: "known" | "unavailable";
|
|
29
|
+
occupiedQuantity?: number;
|
|
30
|
+
resourceId: string;
|
|
31
|
+
resourceNodeId: string;
|
|
32
|
+
}
|
|
33
|
+
export interface FactoryLoadProjectionIssue {
|
|
34
|
+
code: "CONTRADICTORY_RESOURCE_CLAIM" | "CONTRADICTORY_WORK_STATE" | "INVALID_RESOURCE_CAPACITY" | "MISSING_FACTORY" | "RESOURCE_CAPACITY_EXCEEDED" | "UNRESOLVED_RESOURCE_CLAIM" | "UNRESOLVED_WORK_STATE";
|
|
35
|
+
dispatchId?: string;
|
|
36
|
+
id: string;
|
|
37
|
+
message: string;
|
|
38
|
+
reference?: string;
|
|
39
|
+
resourceId?: string;
|
|
40
|
+
workId?: string;
|
|
41
|
+
}
|
|
42
|
+
export interface FactoryLoadProjection {
|
|
43
|
+
issues: FactoryLoadProjectionIssue[];
|
|
44
|
+
resourceOccupancy: FactoryResourceOccupancyProjection[];
|
|
45
|
+
selectedTick: number;
|
|
46
|
+
workStateCounts: FactoryWorkStateCountProjection[];
|
|
47
|
+
}
|
|
48
|
+
export interface FactoryLoadProjectionInput {
|
|
49
|
+
activeDispatches?: readonly FactoryActiveResourceClaimsEvidence[];
|
|
50
|
+
factory?: FactoryDefinition;
|
|
51
|
+
selectedTick: number;
|
|
52
|
+
works?: readonly FactoryWorkStateOccupancyEvidence[];
|
|
53
|
+
}
|
|
54
|
+
export interface FactoryLoadAtTickInput {
|
|
55
|
+
events: readonly FactoryEvent[];
|
|
56
|
+
tick: number;
|
|
57
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import type { FactoryLoadAtTickInput, FactoryLoadProjection } from "./load-contract.js";
|
|
2
|
+
/** Reconstruct Work State counts and resource occupancy at one logical tick. */
|
|
3
|
+
export declare function projectFactoryLoadAtTick(input: FactoryLoadAtTickInput): FactoryLoadProjection;
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
import { projectFactoryLoad } from "./load.js";
|
|
2
|
+
import { canonicalizeFactoryEvents } from "./replay.js";
|
|
3
|
+
/** Reconstruct Work State counts and resource occupancy at one logical tick. */
|
|
4
|
+
export function projectFactoryLoadAtTick(input) {
|
|
5
|
+
const state = {
|
|
6
|
+
activeDispatches: new Map(),
|
|
7
|
+
works: new Map(),
|
|
8
|
+
};
|
|
9
|
+
for (const event of canonicalizeFactoryEvents(input.events)) {
|
|
10
|
+
if (event.context.tick > input.tick)
|
|
11
|
+
break;
|
|
12
|
+
applyLoadEvent(state, event);
|
|
13
|
+
}
|
|
14
|
+
return projectFactoryLoad({
|
|
15
|
+
activeDispatches: [...state.activeDispatches.values()],
|
|
16
|
+
factory: state.factory,
|
|
17
|
+
selectedTick: input.tick,
|
|
18
|
+
works: [...state.works.values()],
|
|
19
|
+
});
|
|
20
|
+
}
|
|
21
|
+
function applyLoadEvent(state, event) {
|
|
22
|
+
const payload = objectRecord(event.payload);
|
|
23
|
+
if (event.type === "INITIAL_STRUCTURE_REQUEST" ||
|
|
24
|
+
event.type === "FACTORY_CHANGE") {
|
|
25
|
+
const factory = objectRecord(payload?.factory);
|
|
26
|
+
if (factory)
|
|
27
|
+
state.factory = structuredClone(factory);
|
|
28
|
+
return;
|
|
29
|
+
}
|
|
30
|
+
if (event.type === "WORK_REQUEST") {
|
|
31
|
+
applyWorkRequest(state, payload);
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
if (event.type === "WORK_STATE_CHANGE") {
|
|
35
|
+
applyWorkStateChange(state, payload);
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
38
|
+
if (event.type === "DISPATCH_REQUEST") {
|
|
39
|
+
applyDispatchRequest(state, event, payload);
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
42
|
+
if (event.type === "DISPATCH_RESPONSE") {
|
|
43
|
+
applyDispatchResponse(state, event, payload);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
function applyWorkRequest(state, payload) {
|
|
47
|
+
for (const value of arrayValue(payload?.works)) {
|
|
48
|
+
const work = workEvidence(value);
|
|
49
|
+
if (!work)
|
|
50
|
+
continue;
|
|
51
|
+
state.works.set(work.id, withInitialState(work, state.factory));
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
function applyWorkStateChange(state, payload) {
|
|
55
|
+
if (typeof payload?.workId !== "string" || !payload.workId)
|
|
56
|
+
return;
|
|
57
|
+
const previous = state.works.get(payload.workId);
|
|
58
|
+
state.works.set(payload.workId, {
|
|
59
|
+
id: payload.workId,
|
|
60
|
+
...(typeof payload.toState === "string"
|
|
61
|
+
? { stateName: payload.toState }
|
|
62
|
+
: {}),
|
|
63
|
+
...(typeof payload.workTypeName === "string"
|
|
64
|
+
? { workTypeId: payload.workTypeName }
|
|
65
|
+
: previous?.workTypeId
|
|
66
|
+
? { workTypeId: previous.workTypeId }
|
|
67
|
+
: {}),
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
function applyDispatchRequest(state, event, payload) {
|
|
71
|
+
const dispatchId = event.context.dispatchId;
|
|
72
|
+
const inputIds = arrayValue(payload?.inputs).flatMap((value) => {
|
|
73
|
+
const input = objectRecord(value);
|
|
74
|
+
return typeof input?.workId === "string" && input.workId
|
|
75
|
+
? [input.workId]
|
|
76
|
+
: [];
|
|
77
|
+
});
|
|
78
|
+
for (const workId of new Set([
|
|
79
|
+
...(event.context.workIds ?? []),
|
|
80
|
+
...inputIds,
|
|
81
|
+
])) {
|
|
82
|
+
state.works.delete(workId);
|
|
83
|
+
}
|
|
84
|
+
if (!dispatchId) {
|
|
85
|
+
state.activeDispatches.set(`incomplete:${event.id}`, {
|
|
86
|
+
id: `incomplete:${event.id}`,
|
|
87
|
+
});
|
|
88
|
+
return;
|
|
89
|
+
}
|
|
90
|
+
const resourceValues = Array.isArray(payload?.resources)
|
|
91
|
+
? payload.resources
|
|
92
|
+
: undefined;
|
|
93
|
+
const resources = resourceValues
|
|
94
|
+
? resourceValues.flatMap((value) => {
|
|
95
|
+
const resource = objectRecord(value);
|
|
96
|
+
return typeof resource?.name === "string" && resource.name
|
|
97
|
+
? [{ resourceName: resource.name }]
|
|
98
|
+
: [];
|
|
99
|
+
})
|
|
100
|
+
: undefined;
|
|
101
|
+
const hasCompleteResourceEvidence = resources !== undefined && resources.length === resourceValues?.length;
|
|
102
|
+
state.activeDispatches.set(dispatchId, {
|
|
103
|
+
id: dispatchId,
|
|
104
|
+
...(hasCompleteResourceEvidence ? { resourceClaims: resources } : {}),
|
|
105
|
+
});
|
|
106
|
+
}
|
|
107
|
+
function applyDispatchResponse(state, event, payload) {
|
|
108
|
+
if (event.context.dispatchId) {
|
|
109
|
+
state.activeDispatches.delete(event.context.dispatchId);
|
|
110
|
+
}
|
|
111
|
+
else {
|
|
112
|
+
state.activeDispatches.set(`incomplete:${event.id}`, {
|
|
113
|
+
id: `incomplete:${event.id}`,
|
|
114
|
+
});
|
|
115
|
+
}
|
|
116
|
+
for (const value of arrayValue(payload?.outputWork)) {
|
|
117
|
+
const work = workEvidence(value);
|
|
118
|
+
if (work)
|
|
119
|
+
state.works.set(work.id, withInitialState(work, state.factory));
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
function workEvidence(value) {
|
|
123
|
+
const work = objectRecord(value);
|
|
124
|
+
if (typeof work?.workId !== "string" || !work.workId)
|
|
125
|
+
return undefined;
|
|
126
|
+
const state = objectRecord(work.state);
|
|
127
|
+
return {
|
|
128
|
+
id: work.workId,
|
|
129
|
+
...(typeof state?.id === "string" ? { stateId: state.id } : {}),
|
|
130
|
+
...(typeof state?.name === "string" ? { stateName: state.name } : {}),
|
|
131
|
+
...(typeof work.workTypeName === "string"
|
|
132
|
+
? { workTypeId: work.workTypeName }
|
|
133
|
+
: {}),
|
|
134
|
+
};
|
|
135
|
+
}
|
|
136
|
+
function withInitialState(work, factory) {
|
|
137
|
+
if (work.stateId || work.stateName || !work.workTypeId)
|
|
138
|
+
return work;
|
|
139
|
+
const workType = factory?.workTypes?.find((candidate) => candidate.name === work.workTypeId || candidate.id === work.workTypeId);
|
|
140
|
+
const initial = workType?.states.find((state) => state.type === "INITIAL");
|
|
141
|
+
return initial
|
|
142
|
+
? {
|
|
143
|
+
...work,
|
|
144
|
+
...(initial.id?.trim() ? { stateId: initial.id } : {}),
|
|
145
|
+
stateName: initial.name,
|
|
146
|
+
}
|
|
147
|
+
: work;
|
|
148
|
+
}
|
|
149
|
+
function objectRecord(value) {
|
|
150
|
+
return value !== null && typeof value === "object"
|
|
151
|
+
? value
|
|
152
|
+
: undefined;
|
|
153
|
+
}
|
|
154
|
+
function arrayValue(value) {
|
|
155
|
+
return Array.isArray(value) ? value : [];
|
|
156
|
+
}
|
package/dist/load.d.ts
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import type { FactoryLoadProjection, FactoryLoadProjectionInput } from "./load-contract.js";
|
|
2
|
+
/** Project selected-tick Work State counts and resource occupancy evidence. */
|
|
3
|
+
export declare function projectFactoryLoad(input: FactoryLoadProjectionInput): FactoryLoadProjection;
|