@sentropic/llm-mesh 0.26.1 → 0.27.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 (49) hide show
  1. package/dist/index.d.ts +2 -0
  2. package/dist/index.d.ts.map +1 -1
  3. package/dist/index.js +2 -0
  4. package/dist/index.js.map +1 -1
  5. package/dist/launch-routing-contract.d.ts +28 -0
  6. package/dist/launch-routing-contract.d.ts.map +1 -0
  7. package/dist/launch-routing-contract.js +70 -0
  8. package/dist/launch-routing-contract.js.map +1 -0
  9. package/dist/ports/route-affinity-store.d.ts +21 -0
  10. package/dist/ports/route-affinity-store.d.ts.map +1 -0
  11. package/dist/ports/route-affinity-store.js +4 -0
  12. package/dist/ports/route-affinity-store.js.map +1 -0
  13. package/dist/route-attempt.d.ts +2 -2
  14. package/dist/route-attempt.d.ts.map +1 -1
  15. package/dist/route-attempt.js +2 -2
  16. package/dist/route-attempt.js.map +1 -1
  17. package/dist/route-planner-state.d.ts +4 -1
  18. package/dist/route-planner-state.d.ts.map +1 -1
  19. package/dist/route-planner-state.js.map +1 -1
  20. package/dist/route-planner.d.ts +5 -2
  21. package/dist/route-planner.d.ts.map +1 -1
  22. package/dist/route-planner.js +69 -35
  23. package/dist/route-planner.js.map +1 -1
  24. package/dist/route-quote.d.ts +1 -1
  25. package/dist/route-quote.d.ts.map +1 -1
  26. package/dist/route-quote.js +11 -1
  27. package/dist/route-quote.js.map +1 -1
  28. package/dist/route-selection.d.ts +1 -1
  29. package/dist/route-selection.d.ts.map +1 -1
  30. package/dist/route-selection.js +18 -9
  31. package/dist/route-selection.js.map +1 -1
  32. package/dist/routing-contracts.d.ts +3 -1
  33. package/dist/routing-contracts.d.ts.map +1 -1
  34. package/dist/routing-policy.js +1 -1
  35. package/dist/routing-policy.js.map +1 -1
  36. package/dist/routing-targets.d.ts +5 -0
  37. package/dist/routing-targets.d.ts.map +1 -1
  38. package/dist/routing-targets.js +28 -4
  39. package/dist/routing-targets.js.map +1 -1
  40. package/dist/transport/codex-runtime-client.d.ts.map +1 -1
  41. package/dist/transport/codex-runtime-client.js +66 -25
  42. package/dist/transport/codex-runtime-client.js.map +1 -1
  43. package/dist/transport/codex-runtime-wire.d.ts.map +1 -1
  44. package/dist/transport/codex-runtime-wire.js +6 -0
  45. package/dist/transport/codex-runtime-wire.js.map +1 -1
  46. package/dist/transport/muse-runtime-client.d.ts.map +1 -1
  47. package/dist/transport/muse-runtime-client.js +22 -0
  48. package/dist/transport/muse-runtime-client.js.map +1 -1
  49. package/package.json +1 -1
package/dist/index.d.ts CHANGED
@@ -16,6 +16,8 @@ export * from './responses-wire.js';
16
16
  export * from './routing-targets.js';
17
17
  export * from './routing-policy.js';
18
18
  export * from './routing-contracts.js';
19
+ export * from './launch-routing-contract.js';
20
+ export * from './ports/route-affinity-store.js';
19
21
  export * from './route-selection.js';
20
22
  export * from './route-planner.js';
21
23
  export { MAX_ROUTE_QUOTE_CANDIDATES, RouteQuoteError, quoteRoute, type RouteQuoteOptions, } from './route-quote.js';
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,yBAAyB,CAAC;AACxC,cAAc,eAAe,CAAC;AAC9B,cAAc,WAAW,CAAC;AAC1B,cAAc,mBAAmB,CAAC;AAClC,cAAc,cAAc,CAAC;AAC7B,cAAc,aAAa,CAAC;AAC5B,cAAc,0BAA0B,CAAC;AACzC,cAAc,yBAAyB,CAAC;AACxC,cAAc,iBAAiB,CAAC;AAChC,cAAc,eAAe,CAAC;AAC9B,cAAc,sBAAsB,CAAC;AACrC,cAAc,WAAW,CAAC;AAC1B,cAAc,gBAAgB,CAAC;AAC/B,cAAc,eAAe,CAAC;AAC9B,cAAc,qBAAqB,CAAC;AACpC,cAAc,sBAAsB,CAAC;AACrC,cAAc,qBAAqB,CAAC;AACpC,cAAc,wBAAwB,CAAC;AACvC,cAAc,sBAAsB,CAAC;AACrC,cAAc,oBAAoB,CAAC;AACnC,OAAO,EACL,0BAA0B,EAAE,eAAe,EAAE,UAAU,EAAE,KAAK,iBAAiB,GAChF,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EAAE,cAAc,EAAE,MAAM,0BAA0B,CAAC;AAC1D,cAAc,YAAY,CAAC;AAC3B,cAAc,qCAAqC,CAAC;AACpD,cAAc,mCAAmC,CAAC;AAClD,cAAc,0CAA0C,CAAC;AACzD,cAAc,2CAA2C,CAAC;AAC1D,cAAc,oCAAoC,CAAC;AACnD,cAAc,uCAAuC,CAAC;AACtD,cAAc,gBAAgB,CAAC;AAC/B,cAAc,YAAY,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,yBAAyB,CAAC;AACxC,cAAc,eAAe,CAAC;AAC9B,cAAc,WAAW,CAAC;AAC1B,cAAc,mBAAmB,CAAC;AAClC,cAAc,cAAc,CAAC;AAC7B,cAAc,aAAa,CAAC;AAC5B,cAAc,0BAA0B,CAAC;AACzC,cAAc,yBAAyB,CAAC;AACxC,cAAc,iBAAiB,CAAC;AAChC,cAAc,eAAe,CAAC;AAC9B,cAAc,sBAAsB,CAAC;AACrC,cAAc,WAAW,CAAC;AAC1B,cAAc,gBAAgB,CAAC;AAC/B,cAAc,eAAe,CAAC;AAC9B,cAAc,qBAAqB,CAAC;AACpC,cAAc,sBAAsB,CAAC;AACrC,cAAc,qBAAqB,CAAC;AACpC,cAAc,wBAAwB,CAAC;AACvC,cAAc,8BAA8B,CAAC;AAC7C,cAAc,iCAAiC,CAAC;AAChD,cAAc,sBAAsB,CAAC;AACrC,cAAc,oBAAoB,CAAC;AACnC,OAAO,EACL,0BAA0B,EAAE,eAAe,EAAE,UAAU,EAAE,KAAK,iBAAiB,GAChF,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EAAE,cAAc,EAAE,MAAM,0BAA0B,CAAC;AAC1D,cAAc,YAAY,CAAC;AAC3B,cAAc,qCAAqC,CAAC;AACpD,cAAc,mCAAmC,CAAC;AAClD,cAAc,0CAA0C,CAAC;AACzD,cAAc,2CAA2C,CAAC;AAC1D,cAAc,oCAAoC,CAAC;AACnD,cAAc,uCAAuC,CAAC;AACtD,cAAc,gBAAgB,CAAC;AAC/B,cAAc,YAAY,CAAC"}
package/dist/index.js CHANGED
@@ -16,6 +16,8 @@ export * from './responses-wire.js';
16
16
  export * from './routing-targets.js';
17
17
  export * from './routing-policy.js';
18
18
  export * from './routing-contracts.js';
19
+ export * from './launch-routing-contract.js';
20
+ export * from './ports/route-affinity-store.js';
19
21
  export * from './route-selection.js';
20
22
  export * from './route-planner.js';
21
23
  export { MAX_ROUTE_QUOTE_CANDIDATES, RouteQuoteError, quoteRoute, } from './route-quote.js';
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,yBAAyB,CAAC;AACxC,cAAc,eAAe,CAAC;AAC9B,cAAc,WAAW,CAAC;AAC1B,cAAc,mBAAmB,CAAC;AAClC,cAAc,cAAc,CAAC;AAC7B,cAAc,aAAa,CAAC;AAC5B,cAAc,0BAA0B,CAAC;AACzC,cAAc,yBAAyB,CAAC;AACxC,cAAc,iBAAiB,CAAC;AAChC,cAAc,eAAe,CAAC;AAC9B,cAAc,sBAAsB,CAAC;AACrC,cAAc,WAAW,CAAC;AAC1B,cAAc,gBAAgB,CAAC;AAC/B,cAAc,eAAe,CAAC;AAC9B,cAAc,qBAAqB,CAAC;AACpC,cAAc,sBAAsB,CAAC;AACrC,cAAc,qBAAqB,CAAC;AACpC,cAAc,wBAAwB,CAAC;AACvC,cAAc,sBAAsB,CAAC;AACrC,cAAc,oBAAoB,CAAC;AACnC,OAAO,EACL,0BAA0B,EAAE,eAAe,EAAE,UAAU,GACxD,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EAAE,cAAc,EAAE,MAAM,0BAA0B,CAAC;AAC1D,cAAc,YAAY,CAAC;AAC3B,cAAc,qCAAqC,CAAC;AACpD,cAAc,mCAAmC,CAAC;AAClD,cAAc,0CAA0C,CAAC;AACzD,cAAc,2CAA2C,CAAC;AAC1D,cAAc,oCAAoC,CAAC;AACnD,cAAc,uCAAuC,CAAC;AACtD,cAAc,gBAAgB,CAAC;AAC/B,cAAc,YAAY,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,yBAAyB,CAAC;AACxC,cAAc,eAAe,CAAC;AAC9B,cAAc,WAAW,CAAC;AAC1B,cAAc,mBAAmB,CAAC;AAClC,cAAc,cAAc,CAAC;AAC7B,cAAc,aAAa,CAAC;AAC5B,cAAc,0BAA0B,CAAC;AACzC,cAAc,yBAAyB,CAAC;AACxC,cAAc,iBAAiB,CAAC;AAChC,cAAc,eAAe,CAAC;AAC9B,cAAc,sBAAsB,CAAC;AACrC,cAAc,WAAW,CAAC;AAC1B,cAAc,gBAAgB,CAAC;AAC/B,cAAc,eAAe,CAAC;AAC9B,cAAc,qBAAqB,CAAC;AACpC,cAAc,sBAAsB,CAAC;AACrC,cAAc,qBAAqB,CAAC;AACpC,cAAc,wBAAwB,CAAC;AACvC,cAAc,8BAA8B,CAAC;AAC7C,cAAc,iCAAiC,CAAC;AAChD,cAAc,sBAAsB,CAAC;AACrC,cAAc,oBAAoB,CAAC;AACnC,OAAO,EACL,0BAA0B,EAAE,eAAe,EAAE,UAAU,GACxD,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EAAE,cAAc,EAAE,MAAM,0BAA0B,CAAC;AAC1D,cAAc,YAAY,CAAC;AAC3B,cAAc,qCAAqC,CAAC;AACpD,cAAc,mCAAmC,CAAC;AAClD,cAAc,0CAA0C,CAAC;AACzD,cAAc,2CAA2C,CAAC;AAC1D,cAAc,oCAAoC,CAAC;AACnD,cAAc,uCAAuC,CAAC;AACtD,cAAc,gBAAgB,CAAC;AAC/B,cAAc,YAAY,CAAC"}
@@ -0,0 +1,28 @@
1
+ import type { TargetMapping } from './routing-targets.js';
2
+ export interface LaunchRoutingContract {
3
+ readonly version: 1;
4
+ readonly revision: string;
5
+ readonly ownerScopeRef: string;
6
+ readonly exactModels: readonly string[];
7
+ readonly authorizedAccountRefs: readonly string[];
8
+ readonly rotateAccounts: boolean;
9
+ readonly effortPolicy: {
10
+ readonly mode: 'preserve';
11
+ readonly allowedEfforts?: readonly string[];
12
+ };
13
+ readonly mapping?: {
14
+ readonly id: string;
15
+ readonly revision: string;
16
+ readonly contentSha256: string;
17
+ readonly targets: Readonly<Record<string, readonly TargetMapping[]>>;
18
+ };
19
+ }
20
+ export declare class LaunchRoutingContractError extends Error {
21
+ readonly code = "routing-contract-invalid";
22
+ constructor();
23
+ }
24
+ export declare const mappingContentSha256: (targets: Readonly<Record<string, readonly TargetMapping[]>>) => string;
25
+ export declare const validateLaunchRoutingContract: (value: unknown) => LaunchRoutingContract;
26
+ export declare const freezeLaunchRoutingContract: (value: unknown) => LaunchRoutingContract;
27
+ export declare const launchTargets: (contract: LaunchRoutingContract, model: string) => readonly TargetMapping[] | undefined;
28
+ //# sourceMappingURL=launch-routing-contract.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"launch-routing-contract.d.ts","sourceRoot":"","sources":["../src/launch-routing-contract.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AAE1D,MAAM,WAAW,qBAAqB;IACpC,QAAQ,CAAC,OAAO,EAAE,CAAC,CAAC;IACpB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;IAC/B,QAAQ,CAAC,WAAW,EAAE,SAAS,MAAM,EAAE,CAAC;IACxC,QAAQ,CAAC,qBAAqB,EAAE,SAAS,MAAM,EAAE,CAAC;IAClD,QAAQ,CAAC,cAAc,EAAE,OAAO,CAAC;IACjC,QAAQ,CAAC,YAAY,EAAE;QAAE,QAAQ,CAAC,IAAI,EAAE,UAAU,CAAC;QAAC,QAAQ,CAAC,cAAc,CAAC,EAAE,SAAS,MAAM,EAAE,CAAA;KAAE,CAAC;IAClG,QAAQ,CAAC,OAAO,CAAC,EAAE;QACjB,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;QACpB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;QAC1B,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;QAC/B,QAAQ,CAAC,OAAO,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,SAAS,aAAa,EAAE,CAAC,CAAC,CAAC;KACtE,CAAC;CACH;AACD,qBAAa,0BAA2B,SAAQ,KAAK;IACnD,QAAQ,CAAC,IAAI,8BAA8B;;CAE5C;AASD,eAAO,MAAM,oBAAoB,YAAa,SAAS,OAAO,MAAM,EAAE,SAAS,aAAa,EAAE,CAAC,CAAC,KAAG,MAI9E,CAAC;AAEtB,eAAO,MAAM,6BAA6B,UAAW,OAAO,KAAG,qBA2B9D,CAAC;AAKF,eAAO,MAAM,2BAA2B,UAAW,OAAO,KAAG,qBACM,CAAC;AACpE,eAAO,MAAM,aAAa,aAAc,qBAAqB,SAAS,MAAM,KAAG,SAAS,aAAa,EAAE,GAAG,SAIzG,CAAC"}
@@ -0,0 +1,70 @@
1
+ import { createHash } from 'node:crypto';
2
+ export class LaunchRoutingContractError extends Error {
3
+ code = 'routing-contract-invalid';
4
+ constructor() { super('Explicit valid launch routing policy is required'); }
5
+ }
6
+ function refuse() { throw new LaunchRoutingContractError(); }
7
+ const record = (value) => Boolean(value) && typeof value === 'object' && !Array.isArray(value);
8
+ const nonempty = (value) => typeof value === 'string' && value.length > 0 && value.length <= 256 && !/[\u0000-\u001f\u007f]/.test(value);
9
+ const keys = (value, allowed) => {
10
+ if (Object.keys(value).some((key) => !allowed.includes(key)))
11
+ refuse();
12
+ };
13
+ const list = (value) => Array.isArray(value) && value.length > 0 && value.length <= 128
14
+ && value.every(nonempty) && new Set(value).size === value.length;
15
+ export const mappingContentSha256 = (targets) => createHash('sha256').update(JSON.stringify(Object.fromEntries(Object.keys(targets).sort().map((model) => [model,
16
+ targets[model].map((target) => ({ providerId: target.providerId, transportProviderId: target.transportProviderId,
17
+ model: target.model, ...(target.effort !== undefined ? { effort: target.effort } : {}) })),
18
+ ])))).digest('hex');
19
+ export const validateLaunchRoutingContract = (value) => {
20
+ if (!record(value))
21
+ refuse();
22
+ keys(value, ['version', 'revision', 'ownerScopeRef', 'exactModels', 'authorizedAccountRefs', 'rotateAccounts', 'effortPolicy', 'mapping']);
23
+ if (value.version !== 1 || !nonempty(value.revision) || !nonempty(value.ownerScopeRef)
24
+ || !list(value.exactModels) || !list(value.authorizedAccountRefs) || typeof value.rotateAccounts !== 'boolean'
25
+ || !record(value.effortPolicy) || value.effortPolicy.mode !== 'preserve')
26
+ refuse();
27
+ keys(value.effortPolicy, ['mode', 'allowedEfforts']);
28
+ if (value.effortPolicy.allowedEfforts !== undefined && !list(value.effortPolicy.allowedEfforts))
29
+ refuse();
30
+ if (value.mapping !== undefined) {
31
+ const mapping = value.mapping;
32
+ if (!record(mapping))
33
+ refuse();
34
+ keys(mapping, ['id', 'revision', 'contentSha256', 'targets']);
35
+ if (!nonempty(mapping.id) || !nonempty(mapping.revision) || !record(mapping.targets)
36
+ || !Object.keys(mapping.targets).length || Object.keys(mapping.targets).length > 128)
37
+ refuse();
38
+ for (const [model, targets] of Object.entries(mapping.targets)) {
39
+ if (!value.exactModels.includes(model) || !Array.isArray(targets) || !targets.length || targets.length > 8)
40
+ refuse();
41
+ for (const target of targets) {
42
+ if (!record(target))
43
+ refuse();
44
+ keys(target, ['providerId', 'transportProviderId', 'model', 'effort']);
45
+ if (!nonempty(target.providerId) || !nonempty(target.transportProviderId) || !nonempty(target.model)
46
+ || (target.effort !== undefined && !nonempty(target.effort)))
47
+ refuse();
48
+ }
49
+ }
50
+ if (typeof mapping.contentSha256 !== 'string' || !/^[a-f0-9]{64}$/.test(mapping.contentSha256)
51
+ || mappingContentSha256(mapping.targets) !== mapping.contentSha256)
52
+ refuse();
53
+ }
54
+ return value;
55
+ };
56
+ const deepFreeze = (value) => {
57
+ if (value && typeof value === 'object') {
58
+ Object.values(value).forEach(deepFreeze);
59
+ Object.freeze(value);
60
+ }
61
+ return value;
62
+ };
63
+ export const freezeLaunchRoutingContract = (value) => deepFreeze(structuredClone(validateLaunchRoutingContract(value)));
64
+ export const launchTargets = (contract, model) => {
65
+ validateLaunchRoutingContract(contract);
66
+ if (!contract.exactModels.includes(model))
67
+ refuse();
68
+ return contract.mapping?.targets[model];
69
+ };
70
+ //# sourceMappingURL=launch-routing-contract.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"launch-routing-contract.js","sourceRoot":"","sources":["../src/launch-routing-contract.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,21 @@
1
+ import type { PlannedRouteTarget } from '../routing-contracts.js';
2
+ export interface RouteAffinityKey {
3
+ readonly ownerScopeRef: string;
4
+ readonly workspaceRef: string;
5
+ readonly clientSessionRef: string;
6
+ readonly policyRevision: string;
7
+ }
8
+ export interface RouteAffinityRecord extends RouteAffinityKey {
9
+ readonly revision: number;
10
+ readonly accountRef: string;
11
+ readonly diagnosticAccountRef: string;
12
+ readonly accountEpoch: number;
13
+ readonly target: PlannedRouteTarget;
14
+ }
15
+ /** Trusted protected storage; a successful CAS durably acknowledges the pin. */
16
+ export interface RouteAffinityStore {
17
+ read(key: RouteAffinityKey): Promise<RouteAffinityRecord | null>;
18
+ compareAndSet(key: RouteAffinityKey, expectedRevision: number, next: RouteAffinityRecord): Promise<void>;
19
+ }
20
+ export declare const routeAffinityRef: (key: RouteAffinityKey) => string;
21
+ //# sourceMappingURL=route-affinity-store.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"route-affinity-store.d.ts","sourceRoot":"","sources":["../../src/ports/route-affinity-store.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,yBAAyB,CAAC;AAClE,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;IAC/B,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;IAC9B,QAAQ,CAAC,gBAAgB,EAAE,MAAM,CAAC;IAClC,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAC;CACjC;AACD,MAAM,WAAW,mBAAoB,SAAQ,gBAAgB;IAC3D,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,oBAAoB,EAAE,MAAM,CAAC;IACtC,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;IAC9B,QAAQ,CAAC,MAAM,EAAE,kBAAkB,CAAC;CACrC;AACD,gFAAgF;AAChF,MAAM,WAAW,kBAAkB;IACjC,IAAI,CAAC,GAAG,EAAE,gBAAgB,GAAG,OAAO,CAAC,mBAAmB,GAAG,IAAI,CAAC,CAAC;IACjE,aAAa,CAAC,GAAG,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,MAAM,EAAE,IAAI,EAAE,mBAAmB,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CAC1G;AACD,eAAO,MAAM,gBAAgB,QAAS,gBAAgB,KAAG,MAEvD,CAAC"}
@@ -0,0 +1,4 @@
1
+ export const routeAffinityRef = (key) => JSON.stringify([
2
+ 'route-affinity/v1', key.ownerScopeRef, key.workspaceRef, key.clientSessionRef, key.policyRevision,
3
+ ]);
4
+ //# sourceMappingURL=route-affinity-store.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"route-affinity-store.js","sourceRoot":"","sources":["../../src/ports/route-affinity-store.ts"],"names":[],"mappings":"AAmBA,MAAM,CAAC,MAAM,gBAAgB,GAAG,CAAC,GAAqB,EAAU,EAAE,CAAC,IAAI,CAAC,SAAS,CAAC;IAChF,mBAAmB,EAAE,GAAG,CAAC,aAAa,EAAE,GAAG,CAAC,YAAY,EAAE,GAAG,CAAC,gBAAgB,EAAE,GAAG,CAAC,cAAc;CACnG,CAAC,CAAC"}
@@ -15,8 +15,8 @@ export declare const prepareStoredRouteAttempt: (input: {
15
15
  readonly modelId: string;
16
16
  }) => boolean;
17
17
  readonly onOutcome: (stored: StoredPlan, candidate: RankedRouteCandidate, classification: RouteFailureClassification) => void;
18
- readonly onCommitted: (stored: StoredPlan, candidate: RankedRouteCandidate) => void;
19
- readonly onSuccess: (stored: StoredPlan, candidate: RankedRouteCandidate) => void;
18
+ readonly onCommitted: (stored: StoredPlan, candidate: RankedRouteCandidate) => void | Promise<void>;
19
+ readonly onSuccess: (stored: StoredPlan, candidate: RankedRouteCandidate) => void | Promise<void>;
20
20
  readonly onCancelled: (stored: StoredPlan, candidate: RankedRouteCandidate) => void;
21
21
  }) => Promise<PreparedRouteAttempt>;
22
22
  //# sourceMappingURL=route-attempt.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"route-attempt.d.ts","sourceRoot":"","sources":["../src/route-attempt.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,sBAAsB,CAAC;AACjE,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,0BAA0B,CAAC;AAG3D,OAAO,KAAK,EACV,oBAAoB,EACpB,KAAK,EACL,oBAAoB,EACpB,0BAA0B,EAC1B,sBAAsB,EACvB,MAAM,wBAAwB,CAAC;AAEhC,eAAO,MAAM,yBAAyB,UAAiB;IACrD,QAAQ,CAAC,MAAM,EAAE,UAAU,GAAG,SAAS,CAAC;IACxC,QAAQ,CAAC,OAAO,EAAE,sBAAsB,CAAC;IACzC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;IAC9B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;IAC9B,QAAQ,CAAC,SAAS,EAAE,oBAAoB,CAAC;IACzC,QAAQ,CAAC,KAAK,EAAE,KAAK,CAAC;IACtB,QAAQ,CAAC,cAAc,CAAC,EAAE,CAAC,MAAM,EAAE;QAAE,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;KAAE,KAAK,OAAO,CAAC;IACzG,QAAQ,CAAC,SAAS,EAAE,CAClB,MAAM,EAAE,UAAU,EAClB,SAAS,EAAE,oBAAoB,EAC/B,cAAc,EAAE,0BAA0B,KACvC,IAAI,CAAC;IACV,QAAQ,CAAC,WAAW,EAAE,CAAC,MAAM,EAAE,UAAU,EAAE,SAAS,EAAE,oBAAoB,KAAK,IAAI,CAAC;IACpF,QAAQ,CAAC,SAAS,EAAE,CAAC,MAAM,EAAE,UAAU,EAAE,SAAS,EAAE,oBAAoB,KAAK,IAAI,CAAC;IAClF,QAAQ,CAAC,WAAW,EAAE,CAAC,MAAM,EAAE,UAAU,EAAE,SAAS,EAAE,oBAAoB,KAAK,IAAI,CAAC;CACrF,KAAG,QAAQ,oBAAoB,CA2E/B,CAAC"}
1
+ {"version":3,"file":"route-attempt.d.ts","sourceRoot":"","sources":["../src/route-attempt.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,sBAAsB,CAAC;AACjE,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,0BAA0B,CAAC;AAG3D,OAAO,KAAK,EACV,oBAAoB,EACpB,KAAK,EACL,oBAAoB,EACpB,0BAA0B,EAC1B,sBAAsB,EACvB,MAAM,wBAAwB,CAAC;AAEhC,eAAO,MAAM,yBAAyB,UAAiB;IACrD,QAAQ,CAAC,MAAM,EAAE,UAAU,GAAG,SAAS,CAAC;IACxC,QAAQ,CAAC,OAAO,EAAE,sBAAsB,CAAC;IACzC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;IAC9B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;IAC9B,QAAQ,CAAC,SAAS,EAAE,oBAAoB,CAAC;IACzC,QAAQ,CAAC,KAAK,EAAE,KAAK,CAAC;IACtB,QAAQ,CAAC,cAAc,CAAC,EAAE,CAAC,MAAM,EAAE;QAAE,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;KAAE,KAAK,OAAO,CAAC;IACzG,QAAQ,CAAC,SAAS,EAAE,CAClB,MAAM,EAAE,UAAU,EAClB,SAAS,EAAE,oBAAoB,EAC/B,cAAc,EAAE,0BAA0B,KACvC,IAAI,CAAC;IACV,QAAQ,CAAC,WAAW,EAAE,CAAC,MAAM,EAAE,UAAU,EAAE,SAAS,EAAE,oBAAoB,KAAK,IAAI,GAAG,QAAQ,IAAI,CAAC,CAAC;IACpG,QAAQ,CAAC,SAAS,EAAE,CAAC,MAAM,EAAE,UAAU,EAAE,SAAS,EAAE,oBAAoB,KAAK,IAAI,GAAG,QAAQ,IAAI,CAAC,CAAC;IAClG,QAAQ,CAAC,WAAW,EAAE,CAAC,MAAM,EAAE,UAAU,EAAE,SAAS,EAAE,oBAAoB,KAAK,IAAI,CAAC;CACrF,KAAG,QAAQ,oBAAoB,CA2E/B,CAAC"}
@@ -55,14 +55,14 @@ export const prepareStoredRouteAttempt = async (input) => {
55
55
  if (committed)
56
56
  return;
57
57
  committed = true;
58
- input.onCommitted(stored, candidate);
58
+ await input.onCommitted(stored, candidate);
59
59
  await attempt.markCommitted();
60
60
  },
61
61
  complete: async (usage) => {
62
62
  if (terminal)
63
63
  return;
64
64
  terminal = true;
65
- input.onSuccess(stored, candidate);
65
+ await input.onSuccess(stored, candidate);
66
66
  await attempt.complete(usage);
67
67
  },
68
68
  releaseCancelled: async () => {
@@ -1 +1 @@
1
- {"version":3,"file":"route-attempt.js","sourceRoot":"","sources":["../src/route-attempt.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"route-attempt.js","sourceRoot":"","sources":["../src/route-attempt.ts"],"names":[],"mappings":"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"}
@@ -2,6 +2,7 @@ import type { ExecutionFeature } from './execution-features.js';
2
2
  import type { RankedRouteCandidate } from './route-selection.js';
3
3
  import type { AffinityDescription, IdFactory, RouteAvailabilityDiagnostic, RoutePlan, VerifiedRoutingSubject } from './routing-contracts.js';
4
4
  import type { RoutePolicy } from './routing-policy.js';
5
+ import type { RouteAffinityKey } from './ports/route-affinity-store.js';
5
6
  export interface StoredCandidate extends RankedRouteCandidate {
6
7
  readonly candidateRef: string;
7
8
  }
@@ -9,7 +10,8 @@ export interface StoredPlan {
9
10
  readonly plan: RoutePlan;
10
11
  readonly subjectRef: string;
11
12
  readonly affinityRef?: string;
12
- readonly affinityRevision?: number;
13
+ readonly affinityStoreKey?: RouteAffinityKey;
14
+ affinityRevision?: number;
13
15
  readonly policyProfileName?: string;
14
16
  readonly policyProfileRevision?: string;
15
17
  readonly hadAffinity: boolean;
@@ -20,6 +22,7 @@ export interface StoredPlan {
20
22
  }
21
23
  export interface StoredAffinity extends AffinityDescription {
22
24
  readonly accountRef: string;
25
+ readonly accountEpoch?: number;
23
26
  readonly workspaceId?: string;
24
27
  }
25
28
  export declare class RoutePlanError extends Error {
@@ -1 +1 @@
1
- {"version":3,"file":"route-planner-state.d.ts","sourceRoot":"","sources":["../src/route-planner-state.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAChE,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,sBAAsB,CAAC;AACjE,OAAO,KAAK,EACV,mBAAmB,EACnB,SAAS,EACT,2BAA2B,EAC3B,SAAS,EACT,sBAAsB,EACvB,MAAM,wBAAwB,CAAC;AAChC,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;AAEvD,MAAM,WAAW,eAAgB,SAAQ,oBAAoB;IAC3D,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;CAC/B;AAED,MAAM,WAAW,UAAU;IACzB,QAAQ,CAAC,IAAI,EAAE,SAAS,CAAC;IACzB,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;IAC9B,QAAQ,CAAC,gBAAgB,CAAC,EAAE,MAAM,CAAC;IACnC,QAAQ,CAAC,iBAAiB,CAAC,EAAE,MAAM,CAAC;IACpC,QAAQ,CAAC,qBAAqB,CAAC,EAAE,MAAM,CAAC;IACxC,QAAQ,CAAC,WAAW,EAAE,OAAO,CAAC;IAC9B,QAAQ,CAAC,UAAU,EAAE,SAAS,eAAe,EAAE,CAAC;IAChD,QAAQ,CAAC,MAAM,EAAE,WAAW,CAAC;IAC7B,QAAQ,CAAC,eAAe,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;IACtC,QAAQ,CAAC,aAAa,CAAC,EAAE,MAAM,CAAC;CACjC;AAED,MAAM,WAAW,cAAe,SAAQ,mBAAmB;IACzD,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;CAC/B;AAED,qBAAa,cAAe,SAAQ,KAAK;IAGrC,QAAQ,CAAC,IAAI,EAAE,UAAU,GAAG,eAAe,GAAG,oBAAoB,GAAG,cAAc,GAAG,cAAc,GAAG,iBAAiB,GAAG,UAAU,GACjI,gBAAgB,GAAG,oBAAoB;IAC3C,QAAQ,CAAC,UAAU,CAAC;IACpB;;;;;;;OAOG;IACH,QAAQ,CAAC,WAAW,CAAC;IACrB;;;;OAIG;IACH,QAAQ,CAAC,sBAAsB,CAAC;gBAlBhC,OAAO,EAAE,MAAM,EACN,IAAI,EAAE,UAAU,GAAG,eAAe,GAAG,oBAAoB,GAAG,cAAc,GAAG,cAAc,GAAG,iBAAiB,GAAG,UAAU,GACjI,gBAAgB,GAAG,oBAAoB,EAClC,UAAU,CAAC,yCAA6B;IACjD;;;;;;;OAOG;IACM,WAAW,CAAC,oDAAwC;IAC7D;;;;OAIG;IACM,sBAAsB,CAAC,yCAA6B;CAKhE;AAED,eAAO,MAAM,UAAU,YAAa,sBAAsB,KAAG,MACJ,CAAC;AAE1D,eAAO,MAAM,eAAe,YAAa,sBAAsB,KAAG,MAC3C,CAAC;AAExB,eAAO,MAAM,WAAW,YACb,sBAAsB,eAClB,MAAM,gBACL,MAAM,KACnB,MAAmF,CAAC;AAEvF,eAAO,MAAM,gBAAgB,SACrB,WAAW,aACN,QAAQ,WAAW,CAAC,KAC9B,WAKD,CAAC;AAEH,qBAAa,mBAAoB,YAAW,SAAS;IACnD,OAAO,CAAC,QAAQ,CAAK;IAErB,IAAI,CAAC,MAAM,EAAE,MAAM,GAAG,WAAW,GAAG,SAAS,GAAG,MAAM;CAIvD"}
1
+ {"version":3,"file":"route-planner-state.d.ts","sourceRoot":"","sources":["../src/route-planner-state.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAChE,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,sBAAsB,CAAC;AACjE,OAAO,KAAK,EACV,mBAAmB,EACnB,SAAS,EACT,2BAA2B,EAC3B,SAAS,EACT,sBAAsB,EACvB,MAAM,wBAAwB,CAAC;AAChC,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;AACvD,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,iCAAiC,CAAC;AAExE,MAAM,WAAW,eAAgB,SAAQ,oBAAoB;IAC3D,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;CAC/B;AAED,MAAM,WAAW,UAAU;IACzB,QAAQ,CAAC,IAAI,EAAE,SAAS,CAAC;IACzB,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;IAC9B,QAAQ,CAAC,gBAAgB,CAAC,EAAE,gBAAgB,CAAC;IAC7C,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,iBAAiB,CAAC,EAAE,MAAM,CAAC;IACpC,QAAQ,CAAC,qBAAqB,CAAC,EAAE,MAAM,CAAC;IACxC,QAAQ,CAAC,WAAW,EAAE,OAAO,CAAC;IAC9B,QAAQ,CAAC,UAAU,EAAE,SAAS,eAAe,EAAE,CAAC;IAChD,QAAQ,CAAC,MAAM,EAAE,WAAW,CAAC;IAC7B,QAAQ,CAAC,eAAe,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;IACtC,QAAQ,CAAC,aAAa,CAAC,EAAE,MAAM,CAAC;CACjC;AAED,MAAM,WAAW,cAAe,SAAQ,mBAAmB;IACzD,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,YAAY,CAAC,EAAE,MAAM,CAAC;IAC/B,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;CAC/B;AAED,qBAAa,cAAe,SAAQ,KAAK;IAGrC,QAAQ,CAAC,IAAI,EAAE,UAAU,GAAG,eAAe,GAAG,oBAAoB,GAAG,cAAc,GAAG,cAAc,GAAG,iBAAiB,GAAG,UAAU,GACjI,gBAAgB,GAAG,oBAAoB;IAC3C,QAAQ,CAAC,UAAU,CAAC;IACpB;;;;;;;OAOG;IACH,QAAQ,CAAC,WAAW,CAAC;IACrB;;;;OAIG;IACH,QAAQ,CAAC,sBAAsB,CAAC;gBAlBhC,OAAO,EAAE,MAAM,EACN,IAAI,EAAE,UAAU,GAAG,eAAe,GAAG,oBAAoB,GAAG,cAAc,GAAG,cAAc,GAAG,iBAAiB,GAAG,UAAU,GACjI,gBAAgB,GAAG,oBAAoB,EAClC,UAAU,CAAC,yCAA6B;IACjD;;;;;;;OAOG;IACM,WAAW,CAAC,oDAAwC;IAC7D;;;;OAIG;IACM,sBAAsB,CAAC,yCAA6B;CAKhE;AAED,eAAO,MAAM,UAAU,YAAa,sBAAsB,KAAG,MACJ,CAAC;AAE1D,eAAO,MAAM,eAAe,YAAa,sBAAsB,KAAG,MAC3C,CAAC;AAExB,eAAO,MAAM,WAAW,YACb,sBAAsB,eAClB,MAAM,gBACL,MAAM,KACnB,MAAmF,CAAC;AAEvF,eAAO,MAAM,gBAAgB,SACrB,WAAW,aACN,QAAQ,WAAW,CAAC,KAC9B,WAKD,CAAC;AAEH,qBAAa,mBAAoB,YAAW,SAAS;IACnD,OAAO,CAAC,QAAQ,CAAK;IAErB,IAAI,CAAC,MAAM,EAAE,MAAM,GAAG,WAAW,GAAG,SAAS,GAAG,MAAM;CAIvD"}
@@ -1 +1 @@
1
- {"version":3,"file":"route-planner-state.js","sourceRoot":"","sources":["../src/route-planner-state.ts"],"names":[],"mappings":"AAkCA,MAAM,OAAO,cAAe,SAAQ,KAAK;IAG5B;IAEA;IASA;IAMA;IAnBX,YACE,OAAe,EACN,IACkC,EAClC,UAAwC;IACjD;;;;;;;OAOG;IACM,WAAoD;IAC7D;;;;OAIG;IACM,sBAAoD;QAE7D,KAAK,CAAC,OAAO,CAAC,CAAC;QAnBN,SAAI,GAAJ,IAAI,CAC8B;QAClC,eAAU,GAAV,UAAU,CAA8B;QASxC,gBAAW,GAAX,WAAW,CAAyC;QAMpD,2BAAsB,GAAtB,sBAAsB,CAA8B;QAG7D,IAAI,CAAC,IAAI,GAAG,gBAAgB,CAAC;IAC/B,CAAC;CACF;AAED,MAAM,CAAC,MAAM,UAAU,GAAG,CAAC,OAA+B,EAAU,EAAE,CACpE,GAAG,OAAO,CAAC,YAAY,SAAS,OAAO,CAAC,aAAa,EAAE,CAAC;AAE1D,MAAM,CAAC,MAAM,eAAe,GAAG,CAAC,OAA+B,EAAU,EAAE,CACzE,OAAO,CAAC,aAAa,CAAC;AAExB,MAAM,CAAC,MAAM,WAAW,GAAG,CACzB,OAA+B,EAC/B,WAAmB,EACnB,WAAoB,EACZ,EAAE,CAAC,CAAC,eAAe,CAAC,OAAO,CAAC,EAAE,WAAW,IAAI,EAAE,EAAE,WAAW,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;AAEvF,MAAM,CAAC,MAAM,gBAAgB,GAAG,CAC9B,IAAiB,EACjB,QAA+B,EAClB,EAAE,CAAC,CAAC;IACjB,GAAG,IAAI;IACP,GAAG,QAAQ;IACX,QAAQ,EAAE,QAAQ,EAAE,QAAQ,IAAI,IAAI,CAAC,QAAQ;IAC7C,KAAK,EAAE,QAAQ,EAAE,KAAK,IAAI,IAAI,CAAC,KAAK;CACrC,CAAC,CAAC;AAEH,MAAM,OAAO,mBAAmB;IACtB,QAAQ,GAAG,CAAC,CAAC;IAErB,IAAI,CAAC,MAAwC;QAC3C,IAAI,CAAC,QAAQ,IAAI,CAAC,CAAC;QACnB,OAAO,GAAG,MAAM,IAAI,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,EAAE,CAAC,EAAE,CAAC;IACnD,CAAC;CACF"}
1
+ {"version":3,"file":"route-planner-state.js","sourceRoot":"","sources":["../src/route-planner-state.ts"],"names":[],"mappings":"AAqCA,MAAM,OAAO,cAAe,SAAQ,KAAK;IAG5B;IAEA;IASA;IAMA;IAnBX,YACE,OAAe,EACN,IACkC,EAClC,UAAwC;IACjD;;;;;;;OAOG;IACM,WAAoD;IAC7D;;;;OAIG;IACM,sBAAoD;QAE7D,KAAK,CAAC,OAAO,CAAC,CAAC;QAnBN,SAAI,GAAJ,IAAI,CAC8B;QAClC,eAAU,GAAV,UAAU,CAA8B;QASxC,gBAAW,GAAX,WAAW,CAAyC;QAMpD,2BAAsB,GAAtB,sBAAsB,CAA8B;QAG7D,IAAI,CAAC,IAAI,GAAG,gBAAgB,CAAC;IAC/B,CAAC;CACF;AAED,MAAM,CAAC,MAAM,UAAU,GAAG,CAAC,OAA+B,EAAU,EAAE,CACpE,GAAG,OAAO,CAAC,YAAY,SAAS,OAAO,CAAC,aAAa,EAAE,CAAC;AAE1D,MAAM,CAAC,MAAM,eAAe,GAAG,CAAC,OAA+B,EAAU,EAAE,CACzE,OAAO,CAAC,aAAa,CAAC;AAExB,MAAM,CAAC,MAAM,WAAW,GAAG,CACzB,OAA+B,EAC/B,WAAmB,EACnB,WAAoB,EACZ,EAAE,CAAC,CAAC,eAAe,CAAC,OAAO,CAAC,EAAE,WAAW,IAAI,EAAE,EAAE,WAAW,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;AAEvF,MAAM,CAAC,MAAM,gBAAgB,GAAG,CAC9B,IAAiB,EACjB,QAA+B,EAClB,EAAE,CAAC,CAAC;IACjB,GAAG,IAAI;IACP,GAAG,QAAQ;IACX,QAAQ,EAAE,QAAQ,EAAE,QAAQ,IAAI,IAAI,CAAC,QAAQ;IAC7C,KAAK,EAAE,QAAQ,EAAE,KAAK,IAAI,IAAI,CAAC,KAAK;CACrC,CAAC,CAAC;AAEH,MAAM,OAAO,mBAAmB;IACtB,QAAQ,GAAG,CAAC,CAAC;IAErB,IAAI,CAAC,MAAwC;QAC3C,IAAI,CAAC,QAAQ,IAAI,CAAC,CAAC;QACnB,OAAO,GAAG,MAAM,IAAI,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,EAAE,CAAC,EAAE,CAAC;IACnD,CAAC;CACF"}
@@ -1,6 +1,7 @@
1
1
  import { type ModelEquivalenceCouncil } from './equivalence-council.js';
2
2
  import type { AccountDirectoryPort, AffinityDescription, Clock, IdFactory, PreparedRouteAttempt, AffinityMutationEvent, RouteAvailabilityDiagnostic, RoutePlan, RoutePlanInput, RoutePlanner, VerifiedRoutingSubject, RouteModelInventoryEntry, RouteQuote, RouteQuoteInput } from './routing-contracts.js';
3
3
  import { InMemoryRoutePolicyProfiles } from './routing-policy.js';
4
+ import type { RouteAffinityStore } from './ports/route-affinity-store.js';
4
5
  export interface InMemoryRoutePlannerOptions {
5
6
  readonly directory: AccountDirectoryPort;
6
7
  readonly council?: ModelEquivalenceCouncil;
@@ -13,6 +14,7 @@ export interface InMemoryRoutePlannerOptions {
13
14
  readonly maximumAffinityEntries?: number;
14
15
  readonly maximumRoundRobinEntries?: number;
15
16
  readonly affinityAudit?: (event: AffinityMutationEvent) => void;
17
+ readonly affinityStore?: RouteAffinityStore;
16
18
  }
17
19
  export declare class InMemoryRoutePlanner implements RoutePlanner {
18
20
  private readonly options;
@@ -36,7 +38,7 @@ export declare class InMemoryRoutePlanner implements RoutePlanner {
36
38
  * Same transport resolution as the plan no-route branch, without planning
37
39
  * side effects — the budget admission path uses it to explain empty quotes.
38
40
  */
39
- describeAvailability(subject: VerifiedRoutingSubject, input: Pick<RoutePlanInput, 'requestedModel' | 'targetCandidatesOverride' | 'explicit'>): Promise<readonly RouteAvailabilityDiagnostic[]>;
41
+ describeAvailability(subject: VerifiedRoutingSubject, input: Pick<RoutePlanInput, 'requestedModel' | 'targetCandidatesOverride' | 'explicit' | 'launchRoutingContract' | 'policyOverride' | 'policyProfile'>): Promise<readonly RouteAvailabilityDiagnostic[]>;
40
42
  private matchingAvailabilityDiagnostics;
41
43
  plan(subject: VerifiedRoutingSubject, input: RoutePlanInput): Promise<RoutePlan>;
42
44
  quote(input: RouteQuoteInput): RouteQuote;
@@ -59,8 +61,9 @@ export declare class InMemoryRoutePlanner implements RoutePlanner {
59
61
  rebindAffinity(subject: VerifiedRoutingSubject, planRef: string, candidateRef: string, expectedRevision?: number): AffinityDescription;
60
62
  resetAffinity(subject: VerifiedRoutingSubject, key: string, expectedRevision?: number, workspaceId?: string): boolean;
61
63
  private mutateAffinity;
64
+ private readDurableAffinity;
65
+ private persistAffinity;
62
66
  private bind;
63
- private isAffinityTarget;
64
67
  private evictPlans;
65
68
  private commitRoundRobin;
66
69
  private releaseRoundRobin;
@@ -1 +1 @@
1
- {"version":3,"file":"route-planner.d.ts","sourceRoot":"","sources":["../src/route-planner.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"route-planner.d.ts","sourceRoot":"","sources":["../src/route-planner.ts"],"names":[],"mappings":"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"}
@@ -6,6 +6,7 @@ import { isNativeMessagesTarget, NATIVE_ANTHROPIC_MESSAGES_MODEL_IDS, validateNa
6
6
  import { resolveRequestedTargets, selectRouteCandidates } from './route-selection.js';
7
7
  import { computeRouteQuoteRef, isQuotedRouteTarget, quoteRoute, resolveQuotePolicy } from './route-quote.js';
8
8
  import { InMemoryRoutePolicyProfiles, resolveRouteStrategy } from './routing-policy.js';
9
+ import { LaunchRoutingContractError } from './launch-routing-contract.js';
9
10
  export class InMemoryRoutePlanner {
10
11
  options;
11
12
  plans = new Map();
@@ -65,7 +66,7 @@ export class InMemoryRoutePlanner {
65
66
  // The model-derived resolution ignores `explicit`, so an explicit
66
67
  // transport restriction wins over it here.
67
68
  const explicitTransport = input.explicit?.transportProviderId;
68
- const resolution = resolveRequestedTargets(input);
69
+ const resolution = resolveRequestedTargets(input, resolveQuotePolicy(input, this.profiles).policy.allowEquivalentModels);
69
70
  const transports = new Set(explicitTransport
70
71
  ? [explicitTransport]
71
72
  : resolution.kind === 'known'
@@ -77,6 +78,8 @@ export class InMemoryRoutePlanner {
77
78
  return diagnostics.filter((entry) => transports.has(entry.transportProviderId));
78
79
  }
79
80
  async plan(subject, input) {
81
+ if (input.launchRoutingContract && input.launchRoutingContract.ownerScopeRef !== subject.ownerScopeRef)
82
+ throw new LaunchRoutingContractError();
80
83
  this.evictPlans();
81
84
  const { profile, policy: resolvedPolicy } = resolveQuotePolicy(input, this.profiles);
82
85
  const { quote } = input;
@@ -88,10 +91,10 @@ export class InMemoryRoutePlanner {
88
91
  const inQuoteTarget = (target) => !quote || isQuotedRouteTarget(quote, target);
89
92
  const inQuote = (candidate) => inQuoteTarget(candidate.target);
90
93
  const strategy = resolveRouteStrategy(policy, input);
91
- const affinity = input.affinityKey
94
+ const affinityStoreKey = await this.readDurableAffinity(subject, input);
95
+ let affinity = input.affinityKey
92
96
  ? this.affinities.get(affinityRef(subject, input.affinityKey, input.workspaceId))
93
97
  : undefined;
94
- let affinityDropped = false;
95
98
  const accounts = await this.options.directory.listEligible(subject);
96
99
  const roundRobinKey = `${routingOwnerRef(subject)}\u001f${input.requestedModel}`;
97
100
  const roundRobinState = strategy.kind === 'round-robin'
@@ -138,38 +141,25 @@ export class InMemoryRoutePlanner {
138
141
  ? accounts.find((entry) => entry.accountRef === affinity.accountRef)
139
142
  : undefined;
140
143
  if (affinity && policy.stickyAccount
141
- && inQuoteTarget(affinity.target)
142
144
  && (!input.nativeMessages || (stickyAccount?.nativeMessages?.contractVersion === 1
143
145
  && stickyAccount.nativeMessages.protocol === 'anthropic-messages'
144
146
  && isNativeMessagesTarget(affinity.target, this.nativeMessagesModelIds)
145
147
  && affinity.target.modelId === input.requestedModel))) {
146
148
  const account = stickyAccount;
147
149
  if (!account) {
148
- // Auto-drop: the affinity account is gone (deleted) — resolve without
149
- // the dead pin instead of failing closed on it. The dropped pin is
150
- // cleared for the rest of planning so the stored plan carries no
151
- // affinity revision and round-robin reservation applies to the
152
- // replacement candidates.
153
- if (input.affinityKey) {
154
- this.affinities.delete(affinityRef(subject, input.affinityKey, input.workspaceId));
155
- }
156
- affinityDropped = true;
157
- candidates = this.visibleCandidates(candidates, inQuote, policy);
150
+ throw new RoutePlanError('Pinned account is unavailable', 'no-route');
158
151
  }
159
152
  else if (account.readiness !== 'ready') {
160
153
  candidates = [];
161
154
  }
162
155
  else {
163
- const sameAccount = candidates.filter((candidate) => candidate.account.accountRef === affinity.accountRef
164
- && !this.isAffinityTarget(candidate, affinity));
156
+ const sameAccount = candidates.filter((candidate) => candidate.account.accountRef === affinity.accountRef);
165
157
  const rotated = policy.rotateEquivalentAccounts
166
158
  ? candidates.filter((candidate) => candidate.account.accountRef !== affinity.accountRef)
167
159
  : [];
168
- const sticky = {
169
- account,
170
- target: { ...affinity.target, requestedModel: input.requestedModel, reason: 'sticky' },
171
- };
172
- candidates = this.visibleCandidates([sticky, ...sameAccount, ...rotated], inQuote, policy);
160
+ // A pin selects an account, never an obsolete model or an unauthorized target.
161
+ candidates = this.visibleCandidates([...sameAccount.map((candidate) => ({ ...candidate,
162
+ target: { ...candidate.target, reason: 'sticky' } })), ...rotated], inQuote, policy);
173
163
  }
174
164
  }
175
165
  else {
@@ -184,9 +174,18 @@ export class InMemoryRoutePlanner {
184
174
  const storedCandidates = candidates.map((candidate) => ({
185
175
  ...candidate, candidateRef: this.ids.next('candidate'),
186
176
  }));
187
- // An auto-dropped pin counts as no affinity from here on: the stored
188
- // plan carries no revision and round-robin treats this as a fresh plan.
189
- const effectiveAffinity = affinityDropped ? undefined : affinity;
177
+ const hadAffinity = Boolean(affinity);
178
+ if (affinityStoreKey && !affinity) {
179
+ const first = storedCandidates[0];
180
+ affinity = { affinityRef: affinityRef(subject, input.affinityKey, input.workspaceId),
181
+ accountRef: first.account.accountRef, diagnosticAccountRef: first.account.diagnosticAccountRef,
182
+ revision: 1, accountEpoch: 1, target: first.target, promoted: false };
183
+ await this.options.affinityStore.compareAndSet(affinityStoreKey, 0, { ...affinityStoreKey,
184
+ revision: 1, accountEpoch: 1, accountRef: affinity.accountRef,
185
+ diagnosticAccountRef: affinity.diagnosticAccountRef, target: affinity.target });
186
+ this.affinities.set(affinity.affinityRef, affinity);
187
+ }
188
+ const effectiveAffinity = affinity;
190
189
  const plan = {
191
190
  planRef,
192
191
  expiresAt: new Date(this.clock.now().getTime() + this.planTtlMs).toISOString(),
@@ -206,7 +205,8 @@ export class InMemoryRoutePlanner {
206
205
  };
207
206
  this.plans.set(planRef, {
208
207
  plan, subjectRef: subjectRef(subject), candidates: storedCandidates, policy,
209
- hadAffinity: Boolean(effectiveAffinity),
208
+ hadAffinity,
209
+ ...(affinityStoreKey ? { affinityStoreKey } : {}),
210
210
  ...(effectiveAffinity ? { affinityRevision: effectiveAffinity.revision } : {}),
211
211
  ...(profile ? {
212
212
  policyProfileName: profile.name,
@@ -267,7 +267,8 @@ export class InMemoryRoutePlanner {
267
267
  const quoted = this.keepQuoted(candidates, inQuote);
268
268
  const visible = quoted.filter((candidate) => !this.health.isSuppressed(candidate));
269
269
  if (visible.length > 0)
270
- return visible.slice(0, policy.maxAttempts);
270
+ return (policy.rotateEquivalentAccounts
271
+ ? visible : visible.filter((candidate) => candidate.account.accountRef === visible[0].account.accountRef)).slice(0, policy.maxAttempts);
271
272
  const [only] = quoted;
272
273
  if (quoted.length === 1 && only && this.health.suppressedDegradable(only))
273
274
  return quoted;
@@ -275,6 +276,11 @@ export class InMemoryRoutePlanner {
275
276
  }
276
277
  async prepareAttempt(subject, planRef, candidateRef, requestId, attemptIndex) {
277
278
  const stored = this.plans.get(planRef);
279
+ if (stored?.affinityStoreKey && this.options.affinityStore) {
280
+ const row = await this.options.affinityStore.read(stored.affinityStoreKey);
281
+ if (!row || row.revision !== stored.affinityRevision)
282
+ throw new RoutePlanError('Durable affinity revision changed', 'conflict');
283
+ }
278
284
  if (stored?.plan.councilRevision !== this.council.revision) {
279
285
  throw new RoutePlanError('Route council revision changed', 'invalid-plan');
280
286
  }
@@ -299,7 +305,7 @@ export class InMemoryRoutePlanner {
299
305
  onCommitted: (activePlan, candidate) => this.bind(activePlan, candidate),
300
306
  onSuccess: (activePlan, candidate) => {
301
307
  this.health.clear(candidate);
302
- this.bind(activePlan, candidate);
308
+ return this.bind(activePlan, candidate);
303
309
  },
304
310
  onCancelled: (activePlan) => this.releaseRoundRobin(activePlan),
305
311
  });
@@ -321,6 +327,8 @@ export class InMemoryRoutePlanner {
321
327
  return this.mutateAffinity(subject, planRef, candidateRef, true, expectedRevision);
322
328
  }
323
329
  resetAffinity(subject, key, expectedRevision, workspaceId) {
330
+ if (this.options.affinityStore)
331
+ throw new RoutePlanError('Durable reset requires a new acknowledged session policy', 'conflict');
324
332
  const ref = affinityRef(subject, key, workspaceId);
325
333
  const current = this.affinities.get(ref);
326
334
  if (!current)
@@ -337,6 +345,8 @@ export class InMemoryRoutePlanner {
337
345
  return removed;
338
346
  }
339
347
  mutateAffinity(subject, planRef, candidateRef, allowRebind, expectedRevision) {
348
+ if (this.options.affinityStore)
349
+ throw new RoutePlanError('Durable mutation requires a new acknowledged session policy', 'conflict');
340
350
  const stored = this.plans.get(planRef);
341
351
  if (!stored || stored.subjectRef !== subjectRef(subject) || !stored.affinityRef) {
342
352
  throw new RoutePlanError('Invalid affinity plan', 'invalid-plan');
@@ -365,14 +375,40 @@ export class InMemoryRoutePlanner {
365
375
  });
366
376
  return next;
367
377
  }
368
- bind(stored, candidate) {
378
+ async readDurableAffinity(subject, input) {
379
+ if (!this.options.affinityStore)
380
+ return undefined;
381
+ const contract = input.launchRoutingContract;
382
+ if (!contract || !input.affinityKey || !input.workspaceId)
383
+ throw new LaunchRoutingContractError();
384
+ const key = { ownerScopeRef: subject.ownerScopeRef, workspaceRef: input.workspaceId,
385
+ clientSessionRef: input.affinityKey, policyRevision: contract.revision };
386
+ const ref = affinityRef(subject, input.affinityKey, input.workspaceId);
387
+ const row = await this.options.affinityStore.read(key);
388
+ if (row)
389
+ this.affinities.set(ref, { ...row, affinityRef: ref, promoted: false });
390
+ else
391
+ this.affinities.delete(ref);
392
+ return key;
393
+ }
394
+ async persistAffinity(stored, next) {
395
+ if (!stored.affinityStoreKey || !this.options.affinityStore)
396
+ return;
397
+ await this.options.affinityStore.compareAndSet(stored.affinityStoreKey, next.revision - 1, {
398
+ ...stored.affinityStoreKey, revision: next.revision, accountRef: next.accountRef,
399
+ diagnosticAccountRef: next.diagnosticAccountRef, accountEpoch: next.accountEpoch ?? 1,
400
+ target: next.target,
401
+ });
402
+ }
403
+ async bind(stored, candidate) {
369
404
  this.commitRoundRobin(stored);
370
405
  if (!stored.affinityRef)
371
406
  return;
372
407
  const current = this.affinities.get(stored.affinityRef);
373
- if (current && stored.policy.fallbackMode !== 'one-way')
408
+ if (current && !stored.affinityStoreKey && stored.policy.fallbackMode !== 'one-way')
374
409
  return;
375
- if (current?.target.providerId === candidate.target.providerId
410
+ if (current?.accountRef === candidate.account.accountRef
411
+ && current.target.providerId === candidate.target.providerId
376
412
  && current.target.modelId === candidate.target.modelId
377
413
  && current.target.transportProviderId === candidate.target.transportProviderId)
378
414
  return;
@@ -381,8 +417,11 @@ export class InMemoryRoutePlanner {
381
417
  affinityRef: stored.affinityRef, accountRef: candidate.account.accountRef,
382
418
  diagnosticAccountRef: candidate.account.diagnosticAccountRef,
383
419
  target: candidate.target, revision: (current?.revision ?? 0) + 1,
420
+ accountEpoch: (current?.accountEpoch ?? 1) + (rebind ? 1 : 0),
384
421
  promoted: Boolean(current && !rebind),
385
422
  };
423
+ await this.persistAffinity(stored, next);
424
+ stored.affinityRevision = next.revision;
386
425
  this.affinities.set(stored.affinityRef, next);
387
426
  this.trim(this.affinities, this.maximumAffinityEntries);
388
427
  if (current)
@@ -392,11 +431,6 @@ export class InMemoryRoutePlanner {
392
431
  nextRevision: next.revision, cacheContinuityRisk: rebind,
393
432
  });
394
433
  }
395
- isAffinityTarget(candidate, affinity) {
396
- return candidate.target.providerId === affinity.target.providerId
397
- && candidate.target.modelId === affinity.target.modelId
398
- && candidate.target.transportProviderId === affinity.target.transportProviderId;
399
- }
400
434
  evictPlans() {
401
435
  const now = this.clock.now().getTime();
402
436
  for (const [ref, stored] of this.plans) {