reploid 0.0.0-stage → 0.1.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 (233) hide show
  1. package/apps/poolday/config/chat-files.json +27 -0
  2. package/apps/poolday/config/partition-generation.js +21 -0
  3. package/apps/poolday/config/partition-policy.json +74 -0
  4. package/apps/poolday/config/partition-releases.json +3 -0
  5. package/apps/poolday/config/work-profile.json +122 -0
  6. package/apps/poolday/host/chat-view.js +47 -0
  7. package/apps/poolday/host/earning/client.js +199 -0
  8. package/apps/poolday/host/earning/contribution.js +168 -0
  9. package/apps/poolday/host/partition-release.js +33 -0
  10. package/apps/poolday/host/work-partition-artifact-source.js +37 -0
  11. package/apps/poolday/host/work-partition-capsule.js +39 -0
  12. package/apps/poolday/host/work-partition-plan.js +167 -0
  13. package/apps/poolday/host/work-partition-tokenizer-guard.js +21 -0
  14. package/apps/poolday/infrastructure/model-storage-lock.js +22 -0
  15. package/apps/poolday/infrastructure/pack-release-storage.js +52 -0
  16. package/apps/poolday/pool/pack-operation.js +161 -0
  17. package/apps/poolday/pool/pack-release-policy.js +44 -0
  18. package/apps/poolday/pool/peer-pack-data-channel.js +217 -0
  19. package/apps/poolday/providers/work-adapter.js +67 -0
  20. package/apps/poolday/providers/work-device.js +33 -0
  21. package/apps/poolday/providers/work-generation-completion.js +55 -0
  22. package/apps/poolday/providers/work-provider.js +209 -0
  23. package/apps/poolday/providers/work-resident-provider.js +113 -0
  24. package/deploy/artifacts/doppler-gpu-0.7.1.tgz +0 -0
  25. package/docs/fleet-service.md +214 -0
  26. package/docs/native-participant.md +534 -0
  27. package/native-delivery.json +1174 -0
  28. package/package.json +29 -4
  29. package/packages/reploid/src/artifacts/binary.js +25 -0
  30. package/packages/reploid/src/artifacts/canonical-json.js +23 -0
  31. package/packages/reploid/src/artifacts/custody/exchange.js +402 -0
  32. package/packages/reploid/src/artifacts/custody/piece-acquisition.js +70 -0
  33. package/packages/reploid/src/artifacts/custody/runtime.js +404 -0
  34. package/packages/reploid/src/artifacts/custody/supply-budget.js +19 -0
  35. package/packages/reploid/src/artifacts/custody-certificate.js +123 -0
  36. package/packages/reploid/src/artifacts/delivery-attestation.js +64 -0
  37. package/packages/reploid/src/artifacts/generation-terminal.js +36 -0
  38. package/packages/reploid/src/artifacts/identity.js +91 -0
  39. package/packages/reploid/src/artifacts/participant-proofs.js +159 -0
  40. package/packages/reploid/src/artifacts/receipt.js +131 -0
  41. package/packages/reploid/src/artifacts/signatures.js +94 -0
  42. package/packages/reploid/src/artifacts/whole-model-receipt.js +42 -0
  43. package/packages/reploid/src/cancellation.js +44 -0
  44. package/packages/reploid/src/config/defaults.json +110 -0
  45. package/packages/reploid/src/config/index.js +209 -0
  46. package/packages/reploid/src/config/request-overrides.json +9 -0
  47. package/packages/reploid/src/config/transport-defaults.json +7 -0
  48. package/packages/reploid/src/hashing.js +30 -0
  49. package/packages/reploid/src/measurement/capacity-utilization.js +321 -0
  50. package/packages/reploid/src/measurement/index.js +5 -0
  51. package/packages/reploid/src/measurement/readiness-records.js +310 -0
  52. package/packages/reploid/src/mesh/computer-identity.js +50 -0
  53. package/packages/reploid/src/mesh/computer-identity.policy.json +8 -0
  54. package/packages/reploid/src/mesh/contribution.js +141 -0
  55. package/packages/reploid/src/mesh/generation-recovery.js +58 -0
  56. package/packages/reploid/src/mesh/gpu-signature-shader.js +27 -0
  57. package/packages/reploid/src/mesh/gpu-signature.js +136 -0
  58. package/packages/reploid/src/mesh/gpu-signature.policy.json +11 -0
  59. package/packages/reploid/src/mesh/index.js +66 -0
  60. package/packages/reploid/src/mesh/partitions/automatic-ordered-partitions.js +540 -0
  61. package/packages/reploid/src/mesh/partitions/automatic-partitions.js +298 -0
  62. package/packages/reploid/src/mesh/partitions/partition-attempt-evidence.js +282 -0
  63. package/packages/reploid/src/mesh/partitions/partition-attempt-queue-policy.json +6 -0
  64. package/packages/reploid/src/mesh/partitions/partition-attempt-queue.js +112 -0
  65. package/packages/reploid/src/mesh/partitions/partition-canonical-contract.js +167 -0
  66. package/packages/reploid/src/mesh/partitions/partition-canonical-execution.js +168 -0
  67. package/packages/reploid/src/mesh/partitions/partition-chat-canonical.js +49 -0
  68. package/packages/reploid/src/mesh/partitions/partition-chat.js +125 -0
  69. package/packages/reploid/src/mesh/partitions/partition-contract.js +192 -0
  70. package/packages/reploid/src/mesh/partitions/partition-device-admission.js +96 -0
  71. package/packages/reploid/src/mesh/partitions/partition-discovery.js +96 -0
  72. package/packages/reploid/src/mesh/partitions/partition-entry-publication.js +183 -0
  73. package/packages/reploid/src/mesh/partitions/partition-entry.js +434 -0
  74. package/packages/reploid/src/mesh/partitions/partition-generation-request.js +33 -0
  75. package/packages/reploid/src/mesh/partitions/partition-grants.js +277 -0
  76. package/packages/reploid/src/mesh/partitions/partition-link.js +103 -0
  77. package/packages/reploid/src/mesh/partitions/partition-network.js +109 -0
  78. package/packages/reploid/src/mesh/partitions/partition-ordered-chat.js +290 -0
  79. package/packages/reploid/src/mesh/partitions/partition-ordered-peer.js +415 -0
  80. package/packages/reploid/src/mesh/partitions/partition-ordered-publication.js +100 -0
  81. package/packages/reploid/src/mesh/partitions/partition-peer.js +314 -0
  82. package/packages/reploid/src/mesh/partitions/partition-placement.js +143 -0
  83. package/packages/reploid/src/mesh/partitions/partition-preparation-progress.js +25 -0
  84. package/packages/reploid/src/mesh/partitions/partition-receiver-policy.json +7 -0
  85. package/packages/reploid/src/mesh/partitions/partition-reservations.js +75 -0
  86. package/packages/reploid/src/mesh/partitions/partition-route-envelope-policy.json +16 -0
  87. package/packages/reploid/src/mesh/partitions/partition-route-envelope.js +74 -0
  88. package/packages/reploid/src/mesh/partitions/partition-route-execution.js +409 -0
  89. package/packages/reploid/src/mesh/partitions/partition-route-network.js +179 -0
  90. package/packages/reploid/src/mesh/partitions/partition-route-ranking.js +268 -0
  91. package/packages/reploid/src/mesh/partitions/partition-runner.js +319 -0
  92. package/packages/reploid/src/mesh/partitions/partition-stage-assignment.js +109 -0
  93. package/packages/reploid/src/mesh/partitions/partition-stage-commit.js +103 -0
  94. package/packages/reploid/src/mesh/partitions/partition-step-receiver.js +291 -0
  95. package/packages/reploid/src/mesh/partitions/partition-summary.js +122 -0
  96. package/packages/reploid/src/mesh/partitions/preparation-owner.js +40 -0
  97. package/packages/reploid/src/mesh/partitions/resident-error-codes.json +10 -0
  98. package/packages/reploid/src/mesh/partitions/resident-partition.js +333 -0
  99. package/packages/reploid/src/mesh/partitions/resident-route.js +46 -0
  100. package/packages/reploid/src/mesh/partitions/signed-resident-factory.js +59 -0
  101. package/packages/reploid/src/mesh/peer-generation.js +896 -0
  102. package/packages/reploid/src/mesh/peer-identity.js +87 -0
  103. package/packages/reploid/src/mesh/placement-beliefs.js +107 -0
  104. package/packages/reploid/src/mesh/preparation-progress.js +46 -0
  105. package/packages/reploid/src/mesh/provider-hardware.js +11 -0
  106. package/packages/reploid/src/mesh/remote-generation-requests.js +53 -0
  107. package/packages/reploid/src/mesh/swarm-coordination.js +140 -0
  108. package/packages/reploid/src/mesh/web-access-network.js +112 -0
  109. package/packages/reploid/src/mesh/web-access-policy.json +17 -0
  110. package/packages/reploid/src/retrieval/citations.js +156 -0
  111. package/packages/reploid/src/retrieval/claim-support.js +113 -0
  112. package/packages/reploid/src/retrieval/costs.js +101 -0
  113. package/packages/reploid/src/retrieval/fixed-provider.js +34 -0
  114. package/packages/reploid/src/retrieval/index.js +20 -0
  115. package/packages/reploid/src/retrieval/lexical-support.js +27 -0
  116. package/packages/reploid/src/retrieval/model-reviewer.js +67 -0
  117. package/packages/reploid/src/retrieval/page-fetch.json +10 -0
  118. package/packages/reploid/src/retrieval/page-text.js +182 -0
  119. package/packages/reploid/src/retrieval/passage.js +37 -0
  120. package/packages/reploid/src/retrieval/policy.json +14 -0
  121. package/packages/reploid/src/retrieval/retrieval.js +23 -0
  122. package/packages/reploid/src/retrieval/review-queue.js +76 -0
  123. package/packages/reploid/src/retrieval/sources.js +263 -0
  124. package/packages/reploid/src/retrieval/support-review.js +155 -0
  125. package/packages/reploid/src/retrieval/text.js +21 -0
  126. package/packages/reploid/src/rules/contribution.rules.json +8 -0
  127. package/packages/reploid/src/rules/routing.rules.json +24 -0
  128. package/packages/reploid/src/transport/assignment.js +571 -0
  129. package/packages/reploid/src/transport/bounded-channel-writer.js +46 -0
  130. package/packages/reploid/src/transport/index.js +11 -0
  131. package/packages/reploid/src/transport/introduction-admission.js +24 -0
  132. package/packages/reploid/src/transport/introduction-bootstrap.js +112 -0
  133. package/packages/reploid/src/transport/introduction-policy.json +12 -0
  134. package/packages/reploid/src/transport/introduction-records.js +90 -0
  135. package/packages/reploid/src/transport/introductions.js +209 -0
  136. package/packages/reploid/src/transport/partition-data-channel.js +421 -0
  137. package/packages/reploid/src/transport/partition-errors.json +16 -0
  138. package/packages/reploid/src/transport/retry-policy.js +36 -0
  139. package/packages/reploid/src/transport/room.js +502 -0
  140. package/packages/reploid/src/transport/sdp-fingerprint.js +5 -0
  141. package/packages/reploid/src/transport/signaling.js +571 -0
  142. package/packages/reploid/src/transport/swarm.js +1247 -0
  143. package/packages/reploid/src/transport/tool-offer-channel.js +137 -0
  144. package/server/browser-roots.js +42 -0
  145. package/server/browser-web-worker.js +94 -0
  146. package/server/chat-completions.js +45 -0
  147. package/server/fleet/browser-bridge.js +145 -0
  148. package/server/fleet/contract.js +42 -0
  149. package/server/fleet/credit-evidence.js +103 -0
  150. package/server/fleet/credit-recovery.js +175 -0
  151. package/server/fleet/credits.js +178 -0
  152. package/server/fleet/execution-admission.js +32 -0
  153. package/server/fleet/native-host.js +817 -0
  154. package/server/fleet/placement.js +132 -0
  155. package/server/fleet/requester.js +346 -0
  156. package/server/fleet/service.js +311 -0
  157. package/server/fleet/terminal-policy.js +83 -0
  158. package/server/fleet/text-api.js +94 -0
  159. package/server/hashing.js +35 -0
  160. package/server/http-request-owner.js +49 -0
  161. package/server/messages.js +57 -0
  162. package/server/native-canonical-epochs.js +90 -0
  163. package/server/native-change-log.js +40 -0
  164. package/server/native-earning.js +203 -0
  165. package/server/native-generation-journal.js +46 -0
  166. package/server/native-model-config.js +57 -0
  167. package/server/native-model-files.js +382 -0
  168. package/server/native-model-origin.js +54 -0
  169. package/server/native-model-origins.json +12 -0
  170. package/server/native-node.js +83 -0
  171. package/server/native-owner/controls.js +162 -0
  172. package/server/native-owner/monitor.js +251 -0
  173. package/server/native-owner/policy.js +273 -0
  174. package/server/native-owner/rate.js +92 -0
  175. package/server/native-participant.js +866 -0
  176. package/server/native-readiness-log.js +30 -0
  177. package/server/native-release-storage.js +64 -0
  178. package/server/native-shader-cache.js +369 -0
  179. package/server/native-supply-budget.js +66 -0
  180. package/server/native-whole-earning.js +67 -0
  181. package/server/native-whole-model-fill.js +182 -0
  182. package/server/native-whole-model.js +130 -0
  183. package/server/public-web-worker.js +66 -0
  184. package/server/responses.js +92 -0
  185. package/server/search/errors.js +47 -0
  186. package/server/search/page-fetch.js +185 -0
  187. package/server/text-api.js +83 -0
  188. package/server/text-recovery.js +45 -0
  189. package/server/text-request-options.js +23 -0
  190. package/server/text-requester.js +231 -0
  191. package/server/token-auth.js +9 -0
  192. package/server/whole-model-requester.js +99 -0
  193. package/shared/config/chat-models.json +4685 -0
  194. package/shared/config/doppler-execution-contracts.js +32 -0
  195. package/shared/config/doppler-execution-contracts.json +43 -0
  196. package/shared/config/doppler-generation-contract.js +223 -0
  197. package/shared/config/doppler-local-models.js +79 -0
  198. package/shared/config/doppler-package.json +7 -0
  199. package/shared/config/model-manifests/a0dc69e917133a82780823d17fc771c0f6b882d7c86c571deeaef45be2ff108b.json +6434 -0
  200. package/shared/config/model-manifests/e985e3cdad60d0cb57d84b436161e66ab5b3f6589a168de6f00b4610e5f1fb59.json +16534 -0
  201. package/shared/config/model-manifests/fa6eb7bbc3ba63571f8cede780b877d9272bde948697af93dd3d33837cb46137.json +7175 -0
  202. package/shared/config/model-pieces/qwen-0-8b-pieces.json +1506 -0
  203. package/shared/config/model-pieces/qwen-2b-pieces.json +4800 -0
  204. package/shared/config/model-pieces/qwen35b-q4_0-pieces.json +28880 -0
  205. package/shared/config/reploid-library.json +49 -0
  206. package/shared/config/swarm-bootstrap.json +21 -0
  207. package/shared/infrastructure/doppler-runtime-service.js +261 -0
  208. package/shared/pool/executable-pack.js +87 -0
  209. package/shared/pool/pack-operation-adapters.js +124 -0
  210. package/shared/pool/pack-operation-policy.js +75 -0
  211. package/shared/pool/pool-config.json +2280 -0
  212. package/shared/vendor/reploid/adapters/doppler-session.js +372 -0
  213. package/shared/vendor/reploid/artifacts/binary.js +25 -0
  214. package/shared/vendor/reploid/artifacts/canonical-json.js +23 -0
  215. package/shared/vendor/reploid/artifacts/delivery-attestation.js +64 -0
  216. package/shared/vendor/reploid/artifacts/identity.js +91 -0
  217. package/shared/vendor/reploid/artifacts/manifest-identity.js +17 -0
  218. package/shared/vendor/reploid/artifacts/participant-proofs.js +159 -0
  219. package/shared/vendor/reploid/artifacts/signatures.js +94 -0
  220. package/shared/vendor/reploid/config/defaults.json +110 -0
  221. package/shared/vendor/reploid/config/index.js +209 -0
  222. package/shared/vendor/reploid/config/request-overrides.json +9 -0
  223. package/shared/vendor/reploid/hashing.js +30 -0
  224. package/shared/vendor/reploid/mesh/generation-context-cache.js +97 -0
  225. package/shared/vendor/reploid/mesh/partitions/partition-attempt-evidence.js +282 -0
  226. package/shared/vendor/reploid/mesh/partitions/partition-contract.js +192 -0
  227. package/shared/vendor/reploid/mesh/partitions/partition-receiver-policy.json +7 -0
  228. package/shared/vendor/reploid/mesh/partitions/partition-route-ranking.js +268 -0
  229. package/shared/vendor/reploid/mesh/preparation-progress.js +46 -0
  230. package/shared/vendor/reploid/mesh/provider-hardware.js +11 -0
  231. package/shared/vendor/reploid/rules/routing.rules.json +24 -0
  232. package/shared/vendor/reploid/transport/partition-errors.json +16 -0
  233. package/README.md +0 -3
@@ -0,0 +1,290 @@
1
+ import { partitionAdapterIdentities, samePartitionAdapters } from './partition-contract.js';
2
+ import { TEXT_GENERATION_CAPABILITY, requireGenerationCapability, boundedGeneration, tokenUsage } from './partition-generation-request.js';
3
+ import { createLayerPartitionRunner } from './partition-runner.js';
4
+ import { orderedPartitionDisclosure } from './partition-grants.js';
5
+ import { assertPartition as assert, assertCapacity, partitionFailure, canonicalPartitionJson, canonicalRouteAttemptBinding,
6
+ validatePartitionRoute } from './partition-contract.js';
7
+ import { validateOrderedPublicationContext, orderedPublicationAttempt, orderedGenerationBounds,
8
+ orderedPublicationRecord } from './partition-ordered-publication.js';
9
+ import { ATTEMPT_QUEUE_POLICY, createPartitionAttemptQueue } from './partition-attempt-queue.js';
10
+ import { ROUTE_ENVELOPE_POLICY, createRouteEnvelope } from './partition-route-envelope.js';
11
+
12
+ /** Existing chat composition for an explicitly prepared, coordinator-mediated route.
13
+ * With a requester publication port, final-stage steps are durably committed by the
14
+ * requester's transaction owner before delivery, in batches bounded by the window the
15
+ * requester declares when it admits the attempt (none declared: every step, before the next).
16
+ * Requests are checked against the route's declared envelope (`ordered.envelope`, otherwise the
17
+ * partitions component's policy) after tokenization and before any stage reservation or grant.
18
+ * The attempt's answer deadline is its grant lifetime less the declared clock allowance.
19
+ * An authorized attempt that finds the route's slots or this coordinator's own stage full waits
20
+ * in `ordered.queue` (shared by a coordinator's routes; otherwise this route's own queue under the
21
+ * partitions component's policy) and reports `queued`. Waiting tokenizes, reserves, grants and
22
+ * discloses nothing; admission happens before the disclosure window is applied, so waiting never
23
+ * extends it, and the route an attempt waits for is the route it runs on. */
24
+ export function createOrderedPartitionChat({ runtime, authority, model, plan, planId, limits,
25
+ grantTtlMs, ordered, authorizeRequester, now = Date.now }) {
26
+ const route = structuredClone(ordered.route), policy = structuredClone(limits), stages = [...ordered.stages];
27
+ const queue = ordered.queue ?? createPartitionAttemptQueue({ policy: ATTEMPT_QUEUE_POLICY });
28
+ const envelope = createRouteEnvelope({ policy: ordered.envelope ?? ROUTE_ENVELOPE_POLICY, route });
29
+ assert(Number.isSafeInteger(authority.maxClockSkewMs) && authority.maxClockSkewMs >= 0, 'Ordered chat requires the declared clock allowance');
30
+ assert(['admit', 'pump'].every(method => typeof queue?.[method] === 'function'), 'Ordered admission queue required');
31
+ // Distinguishes this coordinator incarnation's admissions in requester history.
32
+ const coordinatorEpoch = Array.from(crypto.getRandomValues(new Uint8Array(16)), byte => byte.toString(16).padStart(2, '0')).join('');
33
+ const local = stages[0], coordinatorId = route.stages[0].participantId;
34
+ assert(local.id === coordinatorId && local.id === authority.participantId && typeof local.tokenize === 'function'
35
+ && stages.every(stage => typeof stage.getState === 'function' && typeof stage.subscribe === 'function'),
36
+ 'Ordered chat requires the actual local coordinator and prepared stage ports');
37
+ assert(model.id === route.modelId && model.identity === route.modelIdentity && planId === route.planId
38
+ && Number.isSafeInteger(grantTtlMs) && grantTtlMs > 0, 'Ordered chat model, plan and grant lifetime required');
39
+ assert(canonicalPartitionJson(plan.adapterIdentities || []) === canonicalPartitionJson(partitionAdapterIdentities(model)), 'Partition plan adapter binding mismatch');
40
+ const selection = { id: model.id, name: model.name, identity: model.identity, adapters: structuredClone(model.adapters || []),
41
+ selectionId: model.id + '/partition/' + planId,
42
+ partition: { schema: 'reploid.partition-selection/v2', planId, coordinatorId, route } };
43
+ for (const key of ['provider', 'contextLength', 'quantization', 'size', 'recommended']) {
44
+ if (model[key] !== undefined) selection[key] = structuredClone(model[key]);
45
+ }
46
+ let closed = false, validated = false;
47
+ const preparation = validatePartitionRoute(runtime, plan, route).then(() => { validated = true; });
48
+ preparation.catch(() => {});
49
+ const listeners = new Set(), pending = new Map(), openingDebt = new Map();
50
+ const lifetime = new AbortController();
51
+ const runner = createLayerPartitionRunner({ runtime, plan, route, stages, limits: policy,
52
+ // The coordinator holds this frame between stage ports. The authenticated
53
+ // peer owns its actual outgoing binary transfer in executeStage.
54
+ transport: { transferActivation: async frame => structuredClone(frame) },
55
+ // A grant expires with its attempt's answer window: expiry is the deadline, not a refusal.
56
+ authorize: async request => {
57
+ const { ok, reason } = await authority.check(request.grant, request);
58
+ if (reason === 'expired') throw partitionFailure('ANSWER_DEADLINE_EXCEEDED');
59
+ return ok;
60
+ } });
61
+ const models = () => [{ ...structuredClone(selection), textGeneration: TEXT_GENERATION_CAPABILITY,
62
+ availability: !closed && validated && stages.every(stage => stage.getState().ready) ? 'ready' : 'unavailable',
63
+ providerIds: stages.map(stage => stage.id), placement: 'ordered-layer-partition' }];
64
+ // Stage state changes (a settled reservation, a lost stage) may admit or refuse waiting attempts.
65
+ const notify = () => { for (const listener of listeners) { try { listener(models()); } catch {} } queue.pump(); };
66
+ const unsubscribers = stages.map(stage => stage.subscribe(notify));
67
+ // The route's occupied slots: live admission tickets plus finished attempts whose cleanup has
68
+ // not settled (the runner still counts them). The coordinator's own stage is shared by its
69
+ // routes; tickets not yet holding its reservation are subtracted from what it advertises.
70
+ const queueKey = Symbol('ordered-route');
71
+ const admissible = ({ admitted, opening }) => {
72
+ assertCapacity(!closed && models()[0].availability === 'ready');
73
+ const runnerState = runner.getState();
74
+ const unsettledOpenings = [...openingDebt.keys()].filter(id => !pending.has(id)).length;
75
+ const slots = local.getState().reservations?.availableSlots;
76
+ return admitted + runnerState.unsettled + unsettledOpenings < policy.maxConcurrentAttempts
77
+ && (!Number.isSafeInteger(slots) || slots - opening > 0);
78
+ };
79
+ async function refresh(options) { await preparation; await Promise.all(stages.slice(1).map(stage => stage.refresh(options))); notify(); return models(); }
80
+ async function releaseOpening(identity) {
81
+ await local.closeAttempt({ identity, partitionIndex: 0 }); openingDebt.delete(identity.attemptId);
82
+ }
83
+ // The requester's declared persistence window, or none: then each step commits before the next.
84
+ const persistenceWindow = value => {
85
+ if (value === undefined || value === null) return null;
86
+ assert(value && Number.isSafeInteger(value.maxUnsavedSteps) && value.maxUnsavedSteps > 0
87
+ && Number.isSafeInteger(value.maxUnsavedMs) && value.maxUnsavedMs >= 0 && value.maxUnsavedMs <= 2147483647,
88
+ 'Invalid requester persistence window');
89
+ return { maxUnsavedSteps: value.maxUnsavedSteps, maxUnsavedMs: value.maxUnsavedMs };
90
+ };
91
+ /** Chains each step's record to the conversation head as it is computed and commits records in
92
+ * order off the step path: a commit starts `maxUnsavedMs` after the first uncommitted step, and
93
+ * steps computed during a commit form the next batch. The terminal step waits for its commit, and
94
+ * a step that would leave more than `maxUnsavedSteps` uncommitted waits for the commit before it. */
95
+ function createPublisher({ owner, attempt, bounds, head, window }) {
96
+ let previous = head, base = null, durable = null, error = null, timer = null, uncommitted = 0, draining = null;
97
+ let unsaved = [];
98
+ const commit = async batch => {
99
+ const receipts = batch.length > 1 && typeof owner.publishBatch === 'function'
100
+ ? await owner.publishBatch(batch, { evidence: batch.map(() => []) })
101
+ : await batch.reduce((chain, record) => chain.then(async list => [...list, await owner.publish(record, { evidence: [] })]), Promise.resolve([]));
102
+ assert(Array.isArray(receipts) && receipts.length === batch.length && batch.every((record, index) => {
103
+ const receipt = receipts[index];
104
+ return receipt?.attemptId === record.attemptId && receipt.coordinatorEpoch === record.coordinatorEpoch
105
+ && receipt.sequence === record.sequence && receipt.publicationDigest === record.publicationDigest;
106
+ }), 'Durable ordered publication receipt mismatch');
107
+ durable = batch.at(-1).successor; uncommitted -= batch.length;
108
+ };
109
+ const flush = () => {
110
+ clearTimeout(timer); timer = null;
111
+ draining ??= (async () => {
112
+ try {
113
+ while (unsaved.length) {
114
+ if (error) throw error;
115
+ const batch = unsaved; unsaved = [];
116
+ await commit(batch);
117
+ }
118
+ } catch (cause) { error ??= cause; throw error; }
119
+ finally { draining = null; }
120
+ })();
121
+ return draining;
122
+ };
123
+ return {
124
+ durable: () => durable,
125
+ flush: () => error ? Promise.reject(error) : flush(),
126
+ async step(step) {
127
+ if (error) throw error;
128
+ const record = orderedPublicationRecord({ attempt, bounds, vocabSize: plan.vocabSize, base,
129
+ previousPublicationDigest: previous, metadataDigest: runtime.metadataDigest, step });
130
+ previous = record.publicationDigest; base = record.successor;
131
+ unsaved.push(record); uncommitted++;
132
+ if (step.done || !window || uncommitted > window.maxUnsavedSteps) await flush();
133
+ else if (!draining && !timer) timer = setTimeout(() => { flush().catch(() => {}); }, window.maxUnsavedMs);
134
+ }
135
+ };
136
+ }
137
+ async function generate(request, controls) {
138
+ request = structuredClone(request);
139
+ const requested = requireGenerationCapability(models()[0], request.generationRequest, request.canonical !== undefined);
140
+ assert(!requested || requested.maxOutputTokens <= policy.maxTokens, 'Caller token limit exceeds route budget');
141
+ const owner = controls.publication, published = owner !== undefined;
142
+ assertCapacity(!closed);
143
+ assert(request.canonical === undefined && published === (request.publication !== undefined),
144
+ 'Ordered chat requires ordinary execution and explicit requester publication context');
145
+ const context = published ? validateOrderedPublicationContext(request.publication) : null;
146
+ if (published) assert(owner?.requiresCommitEvidence === true && ['reconcile', 'admitAttempt', 'publish', 'cancel']
147
+ .every(name => typeof owner[name] === 'function') && typeof runtime.metadataDigest === 'function' && context.deadlineAt > now(),
148
+ 'Ordered requester publication, digest contract or deadline unavailable');
149
+ const signal = controls.signal ? AbortSignal.any([lifetime.signal, controls.signal]) : lifetime.signal;
150
+ signal.throwIfAborted();
151
+ assert(request.disclosureExpiresAt === undefined || Number.isSafeInteger(request.disclosureExpiresAt) && request.disclosureExpiresAt > now(),
152
+ 'Requester disclosure deadline expired or invalid');
153
+ assert(request.meshId === authority.meshId && request.model.id === selection.id
154
+ && request.model.identity === selection.identity && samePartitionAdapters(request.model, selection)
155
+ && canonicalPartitionJson(request.model.partition) === canonicalPartitionJson(selection.partition), 'Ordered chat selection mismatch');
156
+ const identity = canonicalRouteAttemptBinding({ schema: 'reploid.partition-attempt/v2', route,
157
+ requesterId: request.participantId, threadId: request.threadId, attemptId: request.attemptId,
158
+ placementGeneration: request.placementGeneration ?? 0 });
159
+ assert(!pending.has(identity.attemptId) && !openingDebt.has(identity.attemptId), 'Ordered chat attempt already pending');
160
+ const completion = Promise.withResolvers(); pending.set(identity.attemptId, completion.promise);
161
+ let grant = null, delegatedToRunner = false, failure, admitted = null, cancellation = null, ticket = null, workload = null, publisher = null;
162
+ // The requester's writer records cancellation of an admitted attempt; a
163
+ // committed terminal record stays completed (the writer never rewrites it).
164
+ const settlePublication = () => {
165
+ if (admitted && !cancellation) cancellation = Promise.resolve()
166
+ .then(() => owner.cancel(admitted.attemptId, admitted.coordinatorEpoch)).then(() => null, error => error);
167
+ return cancellation;
168
+ };
169
+ try {
170
+ if (identity.requesterId !== coordinatorId) assert(typeof authorizeRequester === 'function'
171
+ && await authorizeRequester(request, signal) === true, 'Requester has not authorized this route');
172
+ assert(request.permissions?.sharingScope && request.permissions.sharingScope !== 'local', 'Partition disclosure is disabled');
173
+ await refresh({ signal }); signal.throwIfAborted();
174
+ assertCapacity(models()[0].availability === 'ready');
175
+ // Wait for a slot on this exact route, bounded by policy and by the attempt's own disclosure and
176
+ // publication deadlines. Closing this chat or losing a stage refuses the wait with
177
+ // CAPACITY_UNAVAILABLE; the requester's cancellation removes it with nothing held.
178
+ ticket = await queue.admit({ key: queueKey, attemptId: identity.attemptId, requesterId: identity.requesterId,
179
+ deadlineAt: Math.min(request.disclosureExpiresAt ?? Infinity, context?.deadlineAt ?? Infinity),
180
+ signal: controls.signal, check: admissible,
181
+ onQueued: () => controls.onState?.({ threadId: identity.threadId, attemptId: identity.attemptId, status: 'queued',
182
+ execution: { placement: 'ordered-layer-partition', identity } }) });
183
+ signal.throwIfAborted();
184
+ const disclosure = orderedPartitionDisclosure(identity), expiresAt = Math.min(now() + grantTtlMs, request.disclosureExpiresAt ?? Infinity);
185
+ assert(expiresAt > now(), 'Requester disclosure expired during preparation');
186
+ const approved = await controls.requestApproval({ id: crypto.randomUUID(), threadId: identity.threadId,
187
+ attemptId: identity.attemptId, peerId: coordinatorId, recipientIdentity: coordinatorId,
188
+ modelId: selection.id, modelIdentity: selection.identity, adapterIdentities: partitionAdapterIdentities(selection),
189
+ operation: 'generate-partition', ...structuredClone(selection.partition), disclosure,
190
+ expiresAt, input: { messages: structuredClone(request.messages), ...(requested ? {generationRequest: requested} : {}),
191
+ recipients: [...new Set([...disclosure.tokenRecipients, ...disclosure.outputRecipients])] } });
192
+ signal.throwIfAborted(); assert(approved === true && !closed, 'Ordered disclosure declined or chat closed');
193
+ assert(now() < expiresAt, 'Ordered disclosure expired');
194
+ openingDebt.set(identity.attemptId, identity);
195
+ let input;
196
+ // Tokenizing takes this coordinator stage's reservation; from here it counts in the stage's own slots.
197
+ try { input = await local.tokenize({ messages: request.messages, identity, signal }); } finally { ticket.opened(); }
198
+ signal.throwIfAborted();
199
+ assert(input.modelIdentity === selection.identity && Number.isSafeInteger(input.generation?.maxTokens)
200
+ && input.generation.maxTokens > 0 && input.generation.maxTokens <= policy.maxTokens, 'Doppler tokenization contract mismatch');
201
+ let generation = boundedGeneration(input.generation, requested);
202
+ // Outside the route's declared envelope nothing is reserved, granted or sent to another stage.
203
+ workload = envelope.admit({ attemptId: identity.attemptId, promptTokens: input.tokenIds.length,
204
+ answerTokens: generation.maxTokens, explicit: !!requested });
205
+ if (workload.answerTokens !== generation.maxTokens) generation = { ...generation, maxTokens: workload.answerTokens };
206
+ if (requested) {
207
+ await controls.onUsage?.({threadId: identity.threadId, attemptId: identity.attemptId, ...tokenUsage(input.tokenIds.length, 0, requested)});
208
+ signal.throwIfAborted();
209
+ }
210
+ const generationDigest = await runtime.hashPartitionGeneration(generation);
211
+ const remainingMs = Math.floor(expiresAt - now());
212
+ assert(remainingMs > 0, 'Ordered disclosure expired during preparation');
213
+ grant = await authority.issueOrdered(identity, policy, { approved: true, ttlMs: Math.min(grantTtlMs, remainingMs), disclosure, generationDigest, expiresAt });
214
+ signal.throwIfAborted();
215
+ assert(now() < expiresAt && grant.claim.expiresAt <= expiresAt, 'Ordered disclosure expired during grant signing');
216
+ if (published) {
217
+ // Admit before any reservation or input disclosure to stages.
218
+ const attempt = orderedPublicationAttempt({ binding: identity, coordinatorEpoch,
219
+ publisherEpoch: context.publisherEpoch, generationDigest });
220
+ const bounds = orderedGenerationBounds({ maxTokens: generation.maxTokens, maxOutputCharacters: policy.maxOutputCharacters });
221
+ const state = await owner.reconcile(); signal.throwIfAborted();
222
+ assert(Array.isArray(state?.attempts) && !state.attempts.some(item => item.identity?.attemptId === attempt.attemptId),
223
+ 'Ordered attempt was already admitted; retries require a fresh identity');
224
+ const record = await owner.admitAttempt({ identity: attempt, generationPolicy: bounds, executionIdentity: generationDigest,
225
+ vocabSize: plan.vocabSize, deadlineAt: context.deadlineAt });
226
+ admitted = attempt; signal.addEventListener('abort', settlePublication, { once: true }); signal.throwIfAborted();
227
+ publisher = createPublisher({ owner, attempt, bounds, head: state.head ?? null, window: persistenceWindow(record?.persistence) });
228
+ }
229
+ controls.onState?.({ threadId: identity.threadId, attemptId: identity.attemptId, status: 'executing',
230
+ execution: { placement: 'ordered-layer-partition', identity } });
231
+ let sequence = 0;
232
+ // A charged attempt's signed binding reaches every stage; outputs return for acknowledgment.
233
+ const operation = runner.execute({ identity, tokenIds: input.tokenIds, generation,
234
+ maxTokens: generation.maxTokens, signal,
235
+ ...(request.charge ? { charge: request.charge, acknowledgmentDeadlineAt: expiresAt,
236
+ ...(typeof controls.onOutput === 'function' ? { onOutput: controls.onOutput } : {}) } : {}),
237
+ // Fail at the deadline before any participant within the declared clock allowance sees the grant expire.
238
+ deadlineAt: grant.claim.expiresAt - authority.maxClockSkewMs,
239
+ grants: { execution: stages.map(() => grant), tokenContext: stages.map(() => grant),
240
+ activation: stages.slice(1).map(() => grant), output: grant },
241
+ onActivity: activity => controls.onActivity?.({threadId: identity.threadId, attemptId: identity.attemptId, ...activity}),
242
+ // Published text reaches the requester only as its own durable projection.
243
+ ...(publisher ? { onStep: step => publisher.step(step) }
244
+ : { onDelta: text => controls.onDelta?.({ threadId: identity.threadId, attemptId: identity.attemptId, sequence: sequence++, text }) }) });
245
+ try {
246
+ const result = await operation;
247
+ assert(!publisher || publisher.durable()?.terminal === true, 'Ordered generation ended without its durable terminal publication');
248
+ return { threadId: identity.threadId, attemptId: identity.attemptId, modelId: selection.id,
249
+ modelIdentity: selection.identity, adapterIdentities: partitionAdapterIdentities(selection), content: result.content,
250
+ ...(requested ? {usage: tokenUsage(input.tokenIds.length, result.tokenIds.length, requested)} : {}),
251
+ // Coordinator admission wait for this attempt (0 when a slot was free on arrival).
252
+ execution: { ...result.execution, provider: 'peer', admission: { queuedMs: ticket.queuedMs },
253
+ ...(workload.envelope ? { envelope: workload.envelope } : {}) } };
254
+ } finally {
255
+ // An accepted runner attempt owns cleanup, including failed cleanup and
256
+ // retries. Pre-admission rejection leaves tokenizer ownership here.
257
+ delegatedToRunner = runner.getState().attempts.some(item => item.identity.attemptId === identity.attemptId);
258
+ if (delegatedToRunner) openingDebt.delete(identity.attemptId);
259
+ }
260
+ } catch (error) {
261
+ failure = error;
262
+ // A failed attempt keeps every step it computed: the unsaved window is committed before the
263
+ // writer records the attempt's end. A requester that stopped refuses further publication.
264
+ await publisher?.flush().catch(() => {});
265
+ const cancellationError = await settlePublication();
266
+ if (cancellationError) throw new AggregateError([error, cancellationError], 'Ordered execution and publication settlement failed');
267
+ throw error;
268
+ } finally {
269
+ signal.removeEventListener('abort', settlePublication);
270
+ if (grant) authority.revoke(grant);
271
+ try { if (!delegatedToRunner && openingDebt.has(identity.attemptId)) await releaseOpening(identity); }
272
+ catch (error) { throw new AggregateError(failure ? [failure, error] : [error], 'Ordered chat opening cleanup failed'); }
273
+ finally { pending.delete(identity.attemptId); completion.resolve(); ticket?.release(); if (workload) envelope.release(identity.attemptId); }
274
+ }
275
+ }
276
+ return Object.freeze({ getModels: models,
277
+ // pendingInputs includes attempts waiting for admission: a route with waiting attempts is not idle.
278
+ getState: () => ({ ...runner.getState(), pendingInputs: pending.size, unsettledInputs: openingDebt.size }),
279
+ subscribe(listener) { listeners.add(listener); listener(models()); return () => listeners.delete(listener); }, refresh, generate,
280
+ async close() {
281
+ // Attempts still waiting for this route fail as unavailable capacity; they never move to another route.
282
+ closed = true; queue.pump(); lifetime.abort(new Error('Ordered chat closed')); unsubscribers.forEach(unsubscribe => unsubscribe());
283
+ await Promise.allSettled([...pending.values()]);
284
+ const results = await Promise.allSettled([runner.close(), ...[...openingDebt.values()].map(releaseOpening)]);
285
+ listeners.clear();
286
+ const failures = results.filter(result => result.status === 'rejected').map(result => result.reason);
287
+ if (failures.length) throw new AggregateError(failures, 'Ordered chat cleanup remains unsettled');
288
+ }
289
+ });
290
+ }